react-native/Libraries/CameraRoll/RCTImagePickerManager.m

174 lines
5.6 KiB
Mathematica
Raw Normal View History

/*
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#import "RCTImagePickerManager.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTImageStoreManager.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
@interface RCTImagePickerManager () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@end
@implementation RCTImagePickerManager
{
NSMutableArray<UIImagePickerController *> *_pickers;
NSMutableArray<RCTResponseSenderBlock> *_pickerCallbacks;
NSMutableArray<RCTResponseSenderBlock> *_pickerCancelCallbacks;
}
RCT_EXPORT_MODULE(ImagePickerIOS);
@synthesize bridge = _bridge;
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_METHOD(canRecordVideos:(RCTResponseSenderBlock)callback)
{
NSArray<NSString *> *availableMediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
callback(@[@([availableMediaTypes containsObject:(NSString *)kUTTypeMovie])]);
}
RCT_EXPORT_METHOD(canUseCamera:(RCTResponseSenderBlock)callback)
{
callback(@[@([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])]);
}
RCT_EXPORT_METHOD(openCameraDialog:(NSDictionary *)config
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
{
if (RCTRunningInAppExtension()) {
cancelCallback(@[@"Camera access is unavailable in an app extension"]);
return;
}
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
if ([RCTConvert BOOL:config[@"videoMode"]]) {
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
}
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
[self _presentPicker:imagePicker
successCallback:callback
cancelCallback:cancelCallback];
}
RCT_EXPORT_METHOD(openSelectDialog:(NSDictionary *)config
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
{
if (RCTRunningInAppExtension()) {
cancelCallback(@[@"Image picker is currently unavailable in an app extension"]);
return;
}
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
NSMutableArray<NSString *> *allowedTypes = [NSMutableArray new];
if ([RCTConvert BOOL:config[@"showImages"]]) {
[allowedTypes addObject:(NSString *)kUTTypeImage];
}
if ([RCTConvert BOOL:config[@"showVideos"]]) {
[allowedTypes addObject:(NSString *)kUTTypeMovie];
}
imagePicker.mediaTypes = allowedTypes;
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
[self _presentPicker:imagePicker
successCallback:callback
cancelCallback:cancelCallback];
}
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
BOOL isMovie = [mediaType isEqualToString:(NSString *)kUTTypeMovie];
NSString *key = isMovie ? UIImagePickerControllerMediaURL : UIImagePickerControllerReferenceURL;
NSURL *imageURL = info[key];
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSNumber *width = 0;
NSNumber *height = 0;
if (image) {
height = @(image.size.height);
width = @(image.size.width);
}
if (imageURL) {
[self _dismissPicker:picker args:@[imageURL.absoluteString, RCTNullIfNil(height), RCTNullIfNil(width)]];
return;
}
// This is a newly taken image, and doesn't have a URL yet.
// We need to save it to the image store first.
UIImage *originalImage = info[UIImagePickerControllerOriginalImage];
// WARNING: Using ImageStoreManager may cause a memory leak because the
// image isn't automatically removed from store once we're done using it.
[_bridge.imageStoreManager storeImage:originalImage withBlock:^(NSString *tempImageTag) {
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag, height, width] : nil];
}];
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
}
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self _dismissPicker:picker args:nil];
}
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
- (void)_presentPicker:(UIImagePickerController *)imagePicker
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback
{
if (!_pickers) {
_pickers = [NSMutableArray new];
_pickerCallbacks = [NSMutableArray new];
_pickerCancelCallbacks = [NSMutableArray new];
}
[_pickers addObject:imagePicker];
[_pickerCallbacks addObject:callback];
[_pickerCancelCallbacks addObject:cancelCallback];
UIViewController *rootViewController = RCTPresentedViewController();
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
[rootViewController presentViewController:imagePicker animated:YES completion:nil];
}
Refactored module access to allow for lazy loading Summary: public The `bridge.modules` dictionary provides access to all native modules, but this API requires that every module is initialized in advance so that any module can be accessed. This diff introduces a better API that will allow modules to be initialized lazily as they are needed, and deprecates `bridge.modules` (modules that use it will still work, but should be rewritten to use `bridge.moduleClasses` or `-[bridge moduleForName/Class:` instead. The rules are now as follows: * Any module that overrides `init` or `setBridge:` will be initialized on the main thread when the bridge is created * Any module that implements `constantsToExport:` will be initialized later when the config is exported (the module itself will be initialized on a background queue, but `constantsToExport:` will still be called on the main thread. * All other modules will be initialized lazily when a method is first called on them. These rules may seem slightly arcane, but they have the advantage of not violating any assumptions that may have been made by existing code - any module written under the original assumption that it would be initialized synchronously on the main thread when the bridge is created should still function exactly the same, but modules that avoid overriding `init` or `setBridge:` will now be loaded lazily. I've rewritten most of the standard modules to take advantage of this new lazy loading, with the following results: Out of the 65 modules included in UIExplorer: * 16 are initialized on the main thread when the bridge is created * A further 8 are initialized when the config is exported to JS * The remaining 41 will be initialized lazily on-demand Reviewed By: jspahrsummers Differential Revision: D2677695 fb-gh-sync-id: 507ae7e9fd6b563e89292c7371767c978e928f33
2015-11-25 11:09:00 +00:00
- (void)_dismissPicker:(UIImagePickerController *)picker args:(NSArray *)args
{
NSUInteger index = [_pickers indexOfObject:picker];
RCTResponseSenderBlock successCallback = _pickerCallbacks[index];
RCTResponseSenderBlock cancelCallback = _pickerCancelCallbacks[index];
[_pickers removeObjectAtIndex:index];
[_pickerCallbacks removeObjectAtIndex:index];
[_pickerCancelCallbacks removeObjectAtIndex:index];
UIViewController *rootViewController = RCTPresentedViewController();
[rootViewController dismissViewControllerAnimated:YES completion:nil];
if (args) {
successCallback(args);
} else {
cancelCallback(@[]);
}
}
@end