[FEATURE] Video support

This commit is contained in:
Ivan Pusic 2016-09-11 16:31:51 +02:00
parent 3b7f2c4c21
commit 457c0efbd5
13 changed files with 497 additions and 109 deletions

View File

@ -1,5 +1,5 @@
# react-native-image-crop-picker # react-native-image-crop-picker
iOS/Android image picker with support for camera, multiple images and cropping iOS/Android image picker with support for camera, video compression, multiple images and cropping
## Result ## Result
@ -68,6 +68,7 @@ ImagePicker.clean().then(() => {
| multiple | bool (default false) | Enable or disable multiple image selection | | multiple | bool (default false) | Enable or disable multiple image selection |
| includeBase64 | bool (default false) | Enable or disable returning base64 data with image | | includeBase64 | bool (default false) | Enable or disable returning base64 data with image |
| maxFiles (ios only) | number (default 5) | Max number of files to select when using `multiple` option | | maxFiles (ios only) | number (default 5) | Max number of files to select when using `multiple` option |
| compressVideo (ios only) | number (default true) | When video is selected, compress it and convert it to mp4 |
#### Response Object #### Response Object
@ -95,7 +96,7 @@ react-native link react-native-image-crop-picker
- Add `platform :ios, '8.0'` to Podfile (!important) - Add `platform :ios, '8.0'` to Podfile (!important)
- Add `pod 'RSKImageCropper'` and `pod 'QBImagePickerController'` to Podfile - Add `pod 'RSKImageCropper'` and `pod 'QBImagePickerController'` to Podfile
###### non-cocoapods users ###### non-cocoapods users
- Drag and drop the ios/ImageCropPickerSDK folder to your xcode project. (Make sure Copy items if needed IS ticked) - Drag and drop the ios/ImageCropPickerSDK folder to your xcode project. (Make sure Copy items if needed IS ticked)
@ -141,4 +142,3 @@ It is basically wrapper around few libraries
## License ## License
*MIT* *MIT*

View File

@ -16,5 +16,5 @@ android {
dependencies { dependencies {
compile 'com.facebook.react:react-native:+' compile 'com.facebook.react:react-native:+'
compile 'com.yalantis:ucrop:2.1.2' compile 'com.yalantis:ucrop:2.2.0-native'
} }

View File

@ -7,6 +7,7 @@ import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.media.MediaMetadataRetriever;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
@ -28,6 +29,7 @@ import com.facebook.react.bridge.WritableNativeMap;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.webkit.MimeTypeMap;
import com.yalantis.ucrop.UCrop; import com.yalantis.ucrop.UCrop;
@ -58,6 +60,7 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
private static final String E_CAMERA_IS_NOT_AVAILABLE = "E_CAMERA_IS_NOT_AVAILABLE"; private static final String E_CAMERA_IS_NOT_AVAILABLE = "E_CAMERA_IS_NOT_AVAILABLE";
private static final String E_CANNOT_LAUNCH_CAMERA = "E_CANNOT_LAUNCH_CAMERA"; private static final String E_CANNOT_LAUNCH_CAMERA = "E_CANNOT_LAUNCH_CAMERA";
private static final String E_PERMISSIONS_MISSING = "E_PERMISSIONS_MISSING"; private static final String E_PERMISSIONS_MISSING = "E_PERMISSIONS_MISSING";
private static final String E_ERROR_WHILE_CLEANING_FILES = "E_ERROR_WHILE_CLEANING_FILES";
private Promise mPickerPromise; private Promise mPickerPromise;
private Activity activity; private Activity activity;
@ -77,6 +80,15 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
mReactContext = reactContext; mReactContext = reactContext;
} }
public String getTmpDir() {
String tmpDir = mReactContext.getCacheDir() + "/react-native-image-crop-picker";
Boolean created = new File(tmpDir).mkdir();
System.out.println(tmpDir);
return tmpDir;
}
@Override @Override
public String getName() { public String getName() {
return "ImageCropPicker"; return "ImageCropPicker";
@ -90,14 +102,52 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
cropping = options.hasKey("cropping") ? options.getBoolean("cropping") : cropping; cropping = options.hasKey("cropping") ? options.getBoolean("cropping") : cropping;
} }
@ReactMethod private void deleteRecursive(File fileOrDirectory) {
public void clean(final Promise promise) { if (fileOrDirectory.isDirectory()) {
promise.resolve(null); for (File child : fileOrDirectory.listFiles()) {
deleteRecursive(child);
}
}
fileOrDirectory.delete();
} }
@ReactMethod @ReactMethod
public void cleanSingle(final String path, final Promise promise) { public void clean(final Promise promise) {
promise.resolve(null); try {
File file = new File(this.getTmpDir());
if (!file.exists()) throw new Exception("File does not exist");
this.deleteRecursive(file);
promise.resolve(null);
} catch (Exception ex) {
ex.printStackTrace();
promise.reject(E_ERROR_WHILE_CLEANING_FILES, ex.getMessage());
}
}
@ReactMethod
public void cleanSingle(String path, final Promise promise) {
if (path == null) {
promise.reject(E_ERROR_WHILE_CLEANING_FILES, "Cannot cleanup empty path");
return;
}
try {
final String filePrefix = "file://";
if (path.startsWith(filePrefix)) {
path = path.substring(filePrefix.length());
}
File file = new File(path);
if (!file.exists()) throw new Exception("File does not exist. Path: " + path);
this.deleteRecursive(file);
promise.resolve(null);
} catch (Exception ex) {
ex.printStackTrace();
promise.reject(E_ERROR_WHILE_CLEANING_FILES, ex.getMessage());
}
} }
@ReactMethod @ReactMethod
@ -159,7 +209,13 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
try { try {
final Intent galleryIntent = new Intent(Intent.ACTION_PICK); final Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
if (cropping) {
galleryIntent.setType("image/*");
} else {
galleryIntent.setType("image/*,video/*");
}
galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple); galleryIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple);
galleryIntent.setAction(Intent.ACTION_GET_CONTENT); galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
galleryIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); galleryIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
@ -198,7 +254,50 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
return Base64.encodeToString(bytes, Base64.NO_WRAP); return Base64.encodeToString(bytes, Base64.NO_WRAP);
} }
private WritableMap getImage(Uri uri, boolean resolvePath) { public static String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
public WritableMap getSelection(Uri uri) throws Exception {
String path = RealPathUtil.getRealPathFromURI(activity, uri);
if (path == null || path.isEmpty()) {
throw new Exception("Cannot resolve image path.");
}
String mime = getMimeType(path);
if (mime != null && mime.startsWith("video/")) {
return getVideo(path, mime);
}
return getImage(uri, true);
}
public WritableMap getVideo(String path, String mime) {
WritableMap image = new WritableNativeMap();
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(path);
Bitmap bmp = retriever.getFrameAtTime();
if (bmp != null) {
image.putInt("width", bmp.getWidth());
image.putInt("height", bmp.getHeight());
}
image.putString("path", "file://" + path);
image.putString("mime", mime);
image.putInt("size", (int) new File(path).length());
return image;
}
private WritableMap getImage(Uri uri, boolean resolvePath) throws Exception {
WritableMap image = new WritableNativeMap(); WritableMap image = new WritableNativeMap();
String path = uri.getPath(); String path = uri.getPath();
@ -206,20 +305,19 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
path = RealPathUtil.getRealPathFromURI(activity, uri); path = RealPathUtil.getRealPathFromURI(activity, uri);
} }
if (path == null || path.isEmpty()) {
throw new Exception("Cannot resolve image path.");
}
BitmapFactory.Options options = new BitmapFactory.Options(); BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; options.inJustDecodeBounds = true;
long fileLen = 0;
if (path != null) {
fileLen = new File(path).length();
}
BitmapFactory.decodeFile(path, options); BitmapFactory.decodeFile(path, options);
image.putString("path", "file://" + path); image.putString("path", "file://" + path);
image.putInt("width", options.outWidth); image.putInt("width", options.outWidth);
image.putInt("height", options.outHeight); image.putInt("height", options.outHeight);
image.putString("mime", options.outMimeType); image.putString("mime", options.outMimeType);
image.putInt("size", (int) fileLen); image.putInt("size", (int) new File(path).length());
if (includeBase64) { if (includeBase64) {
image.putString("data", getBase64StringFromFile(path)); image.putString("data", getBase64StringFromFile(path));
@ -232,7 +330,7 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
UCrop.Options options = new UCrop.Options(); UCrop.Options options = new UCrop.Options();
options.setCompressionFormat(Bitmap.CompressFormat.JPEG); options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
UCrop.of(uri, Uri.fromFile(new File(activity.getCacheDir(), UUID.randomUUID().toString() + ".jpg"))) UCrop.of(uri, Uri.fromFile(new File(this.getTmpDir(), UUID.randomUUID().toString() + ".jpg")))
.withMaxResultSize(width, height) .withMaxResultSize(width, height)
.withAspectRatio(width, height) .withAspectRatio(width, height)
.withOptions(options) .withOptions(options)
@ -251,23 +349,36 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
ClipData clipData = data.getClipData(); ClipData clipData = data.getClipData();
WritableArray result = new WritableNativeArray(); WritableArray result = new WritableNativeArray();
// only one image selected try {
if (clipData == null) { // only one image selected
result.pushMap(getImage(data.getData(), true)); if (clipData == null) {
} else { result.pushMap(getSelection(data.getData()));
for (int i = 0; i < clipData.getItemCount(); i++) { } else {
result.pushMap(getImage(clipData.getItemAt(i).getUri(), true)); for (int i = 0; i < clipData.getItemCount(); i++) {
result.pushMap(getSelection(clipData.getItemAt(i).getUri()));
}
} }
mPickerPromise.resolve(result);
} catch (Exception ex) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
} }
mPickerPromise.resolve(result);
} else { } else {
Uri uri = data.getData(); Uri uri = data.getData();
if (cropping && uri != null) { if (uri == null) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "Cannot resolve image url");
}
if (cropping) {
startCropping(uri); startCropping(uri);
} else { } else {
mPickerPromise.resolve(getImage(uri, true)); try {
mPickerPromise.resolve(getSelection(uri));
} catch (Exception ex) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
}
} }
} }
} }
@ -280,15 +391,24 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
if (resultCode == Activity.RESULT_CANCELED) { if (resultCode == Activity.RESULT_CANCELED) {
mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG); mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
} else if (resultCode == Activity.RESULT_OK && mCameraCaptureURI != null) { } else if (resultCode == Activity.RESULT_OK) {
Uri uri = mCameraCaptureURI; Uri uri = mCameraCaptureURI;
if (uri == null) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "Cannot resolve image url");
return;
}
if (cropping) { if (cropping) {
UCrop.Options options = new UCrop.Options(); UCrop.Options options = new UCrop.Options();
options.setCompressionFormat(Bitmap.CompressFormat.JPEG); options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
startCropping(uri); startCropping(uri);
} else { } else {
mPickerPromise.resolve(getImage(uri, true)); try {
mPickerPromise.resolve(getImage(uri, true));
} catch (Exception ex) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
}
} }
} }
} }
@ -297,7 +417,11 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
if (data != null) { if (data != null) {
final Uri resultUri = UCrop.getOutput(data); final Uri resultUri = UCrop.getOutput(data);
if (resultUri != null) { if (resultUri != null) {
mPickerPromise.resolve(getImage(resultUri, false)); try {
mPickerPromise.resolve(getImage(resultUri, false));
} catch (Exception ex) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, ex.getMessage());
}
} else { } else {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "Cannot find image data"); mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "Cannot find image data");
} }
@ -344,7 +468,7 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
private File createNewFile(final boolean forcePictureDirectory) { private File createNewFile(final boolean forcePictureDirectory) {
String filename = "image-" + UUID.randomUUID().toString() + ".jpg"; String filename = "image-" + UUID.randomUUID().toString() + ".jpg";
if (tmpImage && (!forcePictureDirectory)) { if (tmpImage && (!forcePictureDirectory)) {
return new File(mReactContext.getCacheDir(), filename); return new File(this.getTmpDir(), filename);
} else { } else {
File path = Environment.getExternalStoragePublicDirectory( File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES); Environment.DIRECTORY_PICTURES);

View File

@ -88,7 +88,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.example" applicationId "com.example"
minSdkVersion 16 minSdkVersion 18
targetSdkVersion 22 targetSdkVersion 22
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -126,6 +126,7 @@ android {
} }
dependencies { dependencies {
compile project(':react-native-video')
compile project(':react-native-image-crop-picker') compile project(':react-native-image-crop-picker')
compile fileTree(dir: "libs", include: ["*.jar"]) compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.1.1" compile "com.android.support:appcompat-v7:23.1.1"

View File

@ -3,6 +3,7 @@ package com.example;
import android.app.Application; import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.brentvatne.react.ReactVideoPackage;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage; import com.facebook.react.shell.MainReactPackage;
@ -23,6 +24,7 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new ReactVideoPackage(),
new PickerPackage() new PickerPackage()
); );
} }

View File

@ -1,3 +1,4 @@
<resources> <resources>
<string name="app_name">example</string> <string name="app_name">example</string>
</resources> </resources>

View File

@ -5,7 +5,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.1.0' classpath 'com.android.tools.build:gradle:2.1.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files

View File

@ -1,6 +1,6 @@
#Fri Aug 26 00:38:15 CEST 2016 #Sat Sep 10 03:05:34 CEST 2016
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

View File

@ -1,6 +1,8 @@
rootProject.name = 'example' rootProject.name = 'example'
include ':app' include ':app'
include ':react-native-video'
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
include ':react-native-image-crop-picker' include ':react-native-image-crop-picker'
project(':react-native-image-crop-picker').projectDir = new File(settingsDir, '../../android') project(':react-native-image-crop-picker').projectDir = new File(settingsDir, '../../android')

View File

@ -1,7 +1,11 @@
import React, {Component} from 'react'; import React, {Component} from 'react';
import {View, Text, StyleSheet, ScrollView, Image, TouchableOpacity} from 'react-native'; import {
View, Text, StyleSheet, ScrollView,
Image, TouchableOpacity, NativeModules, Dimensions
} from 'react-native';
import Video from 'react-native-video';
import {NativeModules, Dimensions} from 'react-native';
var ImagePicker = NativeModules.ImageCropPicker; var ImagePicker = NativeModules.ImageCropPicker;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -57,7 +61,7 @@ export default class App extends Component {
image: {uri: `data:${image.mime};base64,`+ image.data, width: image.width, height: image.height}, image: {uri: `data:${image.mime};base64,`+ image.data, width: image.width, height: image.height},
images: null images: null
}); });
}).catch(e => {}); }).catch(e => alert(e));
} }
cleanupImages() { cleanupImages() {
@ -83,14 +87,15 @@ export default class App extends Component {
ImagePicker.openPicker({ ImagePicker.openPicker({
width: 300, width: 300,
height: 300, height: 300,
cropping: cropit cropping: cropit,
compressVideo: true
}).then(image => { }).then(image => {
console.log('received image', image); console.log('received image', image);
this.setState({ this.setState({
image: {uri: image.path, width: image.width, height: image.height}, image: {uri: image.path, width: image.width, height: image.height, mime: image.mime},
images: null images: null
}); });
}).catch(e => {}); }).catch(e => alert(e));
} }
pickMultiple() { pickMultiple() {
@ -101,21 +106,54 @@ export default class App extends Component {
image: null, image: null,
images: images.map(i => { images: images.map(i => {
console.log('received image', i); console.log('received image', i);
return {uri: i.path, width: i.width, height: i.height}; return {uri: i.path, width: i.width, height: i.height, mime: i.mime};
}) })
}); });
}).catch(e => {}); }).catch(e => alert(e));
} }
scaledHeight(oldW, oldH, newW) { scaledHeight(oldW, oldH, newW) {
return (oldH / oldW) * newW; return (oldH / oldW) * newW;
} }
renderVideo(uri) {
return <View style={{height: 300, width: 300}}>
<Video source={{uri}}
style={{position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0
}}
rate={1}
paused={false}
volume={1}
muted={false}
resizeMode={'cover'}
onLoad={load => console.log(load)}
onProgress={() => {}}
onEnd={() => { console.log('Done!'); }}
repeat={true} />
</View>;
}
renderImage(image) {
return <Image style={{width: 300, height: 300, resizeMode: 'contain'}} source={image} />
}
renderAsset(image) {
if (image.mime && image.mime.toLowerCase().indexOf('video/') !== -1) {
return this.renderVideo(image.uri);
}
return this.renderImage(image);
}
render() { render() {
return <View style={styles.container}> return <View style={styles.container}>
<ScrollView> <ScrollView>
{this.state.image ? <Image style={{width: 300, height: 300, resizeMode: 'contain'}} source={this.state.image} /> : null} {this.state.image ? this.renderAsset(this.state.image) : null}
{this.state.images ? this.state.images.map(i => <Image key={i.uri} style={{width: 300, height: this.scaledHeight(i.width, i.height, 300)}} source={i} />) : null} {this.state.images ? this.state.images.map(i => <View key={i.uri}>{this.renderAsset(i)}</View>) : null}
</ScrollView> </ScrollView>
<TouchableOpacity onPress={() => this.pickSingleWithCamera(false)} style={styles.button}> <TouchableOpacity onPress={() => this.pickSingleWithCamera(false)} style={styles.button}>

View File

@ -27,6 +27,7 @@
34A9DDBD1D7F43320012B1F5 /* QBImagePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34A9DDB81D7F43220012B1F5 /* QBImagePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34A9DDBD1D7F43320012B1F5 /* QBImagePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34A9DDB81D7F43220012B1F5 /* QBImagePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
34A9DDBE1D7F43320012B1F5 /* RSKImageCropper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34A9DDB91D7F43220012B1F5 /* RSKImageCropper.framework */; }; 34A9DDBE1D7F43320012B1F5 /* RSKImageCropper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34A9DDB91D7F43220012B1F5 /* RSKImageCropper.framework */; };
34A9DDBF1D7F43320012B1F5 /* RSKImageCropper.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34A9DDB91D7F43220012B1F5 /* RSKImageCropper.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 34A9DDBF1D7F43320012B1F5 /* RSKImageCropper.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34A9DDB91D7F43220012B1F5 /* RSKImageCropper.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7165791E23AC464E8943EEF6 /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FE03BA1664084F9C9C188799 /* libRCTVideo.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -94,6 +95,13 @@
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React; remoteInfo = React;
}; };
34534DEB1D8374A6005D9519 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 69779CA3792A4BD79EBAC2CD /* RCTVideo.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTVideo;
};
3460B9DF1D6BA58300CCEC39 /* PBXContainerItemProxy */ = { 3460B9DF1D6BA58300CCEC39 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 3460B9D91D6BA58300CCEC39 /* imageCropPicker.xcodeproj */; containerPortal = 3460B9D91D6BA58300CCEC39 /* imageCropPicker.xcodeproj */;
@ -155,8 +163,10 @@
3460B9D91D6BA58300CCEC39 /* imageCropPicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = imageCropPicker.xcodeproj; path = ../../ios/imageCropPicker.xcodeproj; sourceTree = "<group>"; }; 3460B9D91D6BA58300CCEC39 /* imageCropPicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = imageCropPicker.xcodeproj; path = ../../ios/imageCropPicker.xcodeproj; sourceTree = "<group>"; };
34A9DDB81D7F43220012B1F5 /* QBImagePicker.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = QBImagePicker.framework; sourceTree = "<group>"; }; 34A9DDB81D7F43220012B1F5 /* QBImagePicker.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = QBImagePicker.framework; sourceTree = "<group>"; };
34A9DDB91D7F43220012B1F5 /* RSKImageCropper.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RSKImageCropper.framework; sourceTree = "<group>"; }; 34A9DDB91D7F43220012B1F5 /* RSKImageCropper.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RSKImageCropper.framework; sourceTree = "<group>"; };
69779CA3792A4BD79EBAC2CD /* RCTVideo.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTVideo.xcodeproj; path = "../node_modules/react-native-video/RCTVideo.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
FE03BA1664084F9C9C188799 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTVideo.a; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -185,6 +195,7 @@
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */,
34A9DDBC1D7F43320012B1F5 /* QBImagePicker.framework in Frameworks */, 34A9DDBC1D7F43320012B1F5 /* QBImagePicker.framework in Frameworks */,
7165791E23AC464E8943EEF6 /* libRCTVideo.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -286,6 +297,14 @@
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
34534DDF1D8374A6005D9519 /* Products */ = {
isa = PBXGroup;
children = (
34534DEC1D8374A6005D9519 /* libRCTVideo.a */,
);
name = Products;
sourceTree = "<group>";
};
3460B9DA1D6BA58300CCEC39 /* Products */ = { 3460B9DA1D6BA58300CCEC39 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -325,6 +344,7 @@
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */,
69779CA3792A4BD79EBAC2CD /* RCTVideo.xcodeproj */,
); );
name = Libraries; name = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
@ -405,7 +425,7 @@
83CBB9F71A601CBA00E9B192 /* Project object */ = { 83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0730; LastUpgradeCheck = 730;
ORGANIZATIONNAME = Facebook; ORGANIZATIONNAME = Facebook;
TargetAttributes = { TargetAttributes = {
00E356ED1AD99517003FC87E = { 00E356ED1AD99517003FC87E = {
@ -465,6 +485,10 @@
ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */;
ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */;
}, },
{
ProductGroup = 34534DDF1D8374A6005D9519 /* Products */;
ProjectRef = 69779CA3792A4BD79EBAC2CD /* RCTVideo.xcodeproj */;
},
{ {
ProductGroup = 139FDEE71B06529A00C62182 /* Products */; ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
@ -539,6 +563,13 @@
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR; sourceTree = BUILT_PRODUCTS_DIR;
}; };
34534DEC1D8374A6005D9519 /* libRCTVideo.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTVideo.a;
remoteRef = 34534DEB1D8374A6005D9519 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
3460B9E01D6BA58300CCEC39 /* libimageCropPicker.a */ = { 3460B9E01D6BA58300CCEC39 /* libimageCropPicker.a */ = {
isa = PBXReferenceProxy; isa = PBXReferenceProxy;
fileType = archive.ar; fileType = archive.ar;
@ -650,6 +681,10 @@
INFOPLIST_FILE = exampleTests/Info.plist; INFOPLIST_FILE = exampleTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2; IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example";
@ -664,6 +699,10 @@
INFOPLIST_FILE = exampleTests/Info.plist; INFOPLIST_FILE = exampleTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2; IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example";
@ -685,6 +724,7 @@
"$(inherited)", "$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-video",
); );
INFOPLIST_FILE = example/Info.plist; INFOPLIST_FILE = example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -713,6 +753,7 @@
"$(inherited)", "$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-video",
); );
INFOPLIST_FILE = example/Info.plist; INFOPLIST_FILE = example/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@ -766,6 +807,7 @@
"$(inherited)", "$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-video",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
@ -806,6 +848,7 @@
"$(inherited)", "$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**", "$(SRCROOT)/../node_modules/react-native/React/**",
"$(SRCROOT)/../node_modules/react-native-video",
); );
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;

View File

@ -22,6 +22,9 @@
#define ERROR_CANNOT_SAVE_IMAGE_KEY @"cannot_save_image" #define ERROR_CANNOT_SAVE_IMAGE_KEY @"cannot_save_image"
#define ERROR_CANNOT_SAVE_IMAGE_MSG @"Cannot save image. Unable to write to tmp location." #define ERROR_CANNOT_SAVE_IMAGE_MSG @"Cannot save image. Unable to write to tmp location."
#define ERROR_CANNOT_PROCESS_VIDEO_KEY @"cannot_process_video"
#define ERROR_CANNOT_PROCESS_VIDEO_MSG @"Cannot process video data"
@implementation ImageCropPicker @implementation ImageCropPicker
RCT_EXPORT_MODULE(); RCT_EXPORT_MODULE();
@ -33,6 +36,7 @@ RCT_EXPORT_MODULE();
@"multiple": @NO, @"multiple": @NO,
@"cropping": @NO, @"cropping": @NO,
@"includeBase64": @NO, @"includeBase64": @NO,
@"compressVideo": @YES,
@"maxFiles": @5, @"maxFiles": @5,
@"width": @200, @"width": @200,
@"height": @200 @"height": @200
@ -117,8 +121,17 @@ RCT_EXPORT_METHOD(openCamera:(NSDictionary *)options
} }
- (NSString*) getTmpDirectory { - (NSString*) getTmpDirectory {
NSString* TMP_DIRECTORY = @"/react-native-image-crop-picker/"; NSString *TMP_DIRECTORY = @"react-native-image-crop-picker/";
return [NSTemporaryDirectory() stringByAppendingString:TMP_DIRECTORY]; NSString *tmpFullPath = [NSTemporaryDirectory() stringByAppendingString:TMP_DIRECTORY];
BOOL isDir;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:tmpFullPath isDirectory:&isDir];
if (!exists) {
[[NSFileManager defaultManager] createDirectoryAtPath: tmpFullPath
withIntermediateDirectories:YES attributes:nil error:nil];
}
return tmpFullPath;
} }
- (BOOL)cleanTmpDirectory { - (BOOL)cleanTmpDirectory {
@ -172,7 +185,12 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
imagePickerController.allowsMultipleSelection = [[self.options objectForKey:@"multiple"] boolValue]; imagePickerController.allowsMultipleSelection = [[self.options objectForKey:@"multiple"] boolValue];
imagePickerController.maximumNumberOfSelection = [[self.options objectForKey:@"maxFiles"] intValue]; imagePickerController.maximumNumberOfSelection = [[self.options objectForKey:@"maxFiles"] intValue];
imagePickerController.showsNumberOfSelectedAssets = YES; imagePickerController.showsNumberOfSelectedAssets = YES;
imagePickerController.mediaType = QBImagePickerMediaTypeImage;
if ([[self.options objectForKey:@"cropping"] boolValue]) {
imagePickerController.mediaType = QBImagePickerMediaTypeImage;
} else {
imagePickerController.mediaType = QBImagePickerMediaTypeAny;
}
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
[[self getRootVC] presentViewController:imagePickerController animated:YES completion:nil]; [[self getRootVC] presentViewController:imagePickerController animated:YES completion:nil];
@ -181,6 +199,121 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
}]; }];
} }
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL
outputURL:(NSURL*)outputURL
handler:(void (^)(AVAssetExportSession*))handler {
[[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetMediumQuality];
exportSession.outputURL = outputURL;
exportSession.outputFileType = AVFileTypeMPEG4;
[exportSession exportAsynchronouslyWithCompletionHandler:^(void)
{
handler(exportSession);
}];
}
- (void)showActivityIndicator:(void (^)(UIActivityIndicatorView*, UIView*))handler {
UIView *mainView = [[self getRootVC] view];
// create overlay
UIView *loadingView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
loadingView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
loadingView.clipsToBounds = YES;
// create loading spinner
UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityView.frame = CGRectMake(65, 40, activityView.bounds.size.width, activityView.bounds.size.height);
activityView.center = loadingView.center;
[loadingView addSubview:activityView];
// create message
UILabel *loadingLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 115, 130, 22)];
loadingLabel.backgroundColor = [UIColor clearColor];
loadingLabel.textColor = [UIColor whiteColor];
loadingLabel.adjustsFontSizeToFitWidth = YES;
CGPoint loadingLabelLocation = loadingView.center;
loadingLabelLocation.y += [activityView bounds].size.height;
loadingLabel.center = loadingLabelLocation;
loadingLabel.textAlignment = UITextAlignmentCenter;
loadingLabel.text = @"Processing assets...";
[loadingLabel setFont:[UIFont boldSystemFontOfSize:18]];
[loadingView addSubview:loadingLabel];
// show all
[mainView addSubview:loadingView];
[activityView startAnimating];
handler(activityView, loadingView);
}
- (void) getVideoAsset:(PHAsset*)forAsset completion:(void (^)(NSDictionary* image))completion {
PHImageManager *manager = [PHImageManager defaultManager];
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionOriginal;
[manager
requestAVAssetForVideo:forAsset
options:options
resultHandler:^(AVAsset * asset, AVAudioMix * audioMix,
NSDictionary *info) {
NSURL *sourceURL = [(AVURLAsset *)asset URL];
AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
CGSize dimensions = CGSizeApplyAffineTransform(track.naturalSize, track.preferredTransform);
if (![[self.options objectForKey:@"compressVideo"] boolValue]) {
NSNumber *fileSizeValue = nil;
[sourceURL getResourceValue:&fileSizeValue
forKey:NSURLFileSizeKey
error:nil];
completion([self createAttachmentResponse:[sourceURL absoluteString]
withWidth:[NSNumber numberWithFloat:dimensions.width]
withHeight:[NSNumber numberWithFloat:dimensions.height]
withMime:[@"video/" stringByAppendingString:[[sourceURL pathExtension] lowercaseString]]
withSize:fileSizeValue
withData:[NSNull null]]);
return;
}
// create temp file
NSString *tmpDirFullPath = [self getTmpDirectory];
NSString *filePath = [tmpDirFullPath stringByAppendingString:[[NSUUID UUID] UUIDString]];
filePath = [filePath stringByAppendingString:@".mp4"];
NSURL *outputURL = [NSURL fileURLWithPath:filePath];
[self convertVideoToLowQuailtyWithInputURL:sourceURL outputURL:outputURL handler:^(AVAssetExportSession *exportSession) {
if (exportSession.status == AVAssetExportSessionStatusCompleted) {
NSNumber *fileSizeValue = nil;
[outputURL getResourceValue:&fileSizeValue
forKey:NSURLFileSizeKey
error:nil];
completion([self createAttachmentResponse:[outputURL absoluteString]
withWidth:[NSNumber numberWithFloat:dimensions.width]
withHeight:[NSNumber numberWithFloat:dimensions.height]
withMime:@"video/mp4"
withSize:fileSizeValue
withData:[NSNull null]]);
} else {
completion(nil);
}
}];
}];
}
- (NSDictionary*) createAttachmentResponse:(NSString*)filePath withWidth:(NSNumber*)width withHeight:(NSNumber*)height withMime:(NSString*)mime withSize:(NSNumber*)size withData:(NSString*)data {
return @{
@"path": filePath,
@"width": width,
@"height": height,
@"mime": mime,
@"size": size,
@"data": data,
};
}
- (void)qb_imagePickerController: - (void)qb_imagePickerController:
(QBImagePickerController *)imagePickerController (QBImagePickerController *)imagePickerController
didFinishPickingAssets:(NSArray *)assets { didFinishPickingAssets:(NSArray *)assets {
@ -188,47 +321,101 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
PHImageManager *manager = [PHImageManager defaultManager]; PHImageManager *manager = [PHImageManager defaultManager];
if ([[[self options] objectForKey:@"multiple"] boolValue]) { if ([[[self options] objectForKey:@"multiple"] boolValue]) {
NSMutableArray *images = [[NSMutableArray alloc] init]; NSMutableArray *selections = [[NSMutableArray alloc] init];
PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init]; PHImageRequestOptions* options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES; options.synchronous = YES;
for (PHAsset *asset in assets) { [self showActivityIndicator:^(UIActivityIndicatorView *indicatorView, UIView *overlayView) {
NSLock *lock = [[NSLock alloc] init];
__block int processed = 0;
for (PHAsset *phAsset in assets) {
if (phAsset.mediaType == PHAssetMediaTypeVideo) {
[self getVideoAsset:phAsset completion:^(NSDictionary* video) {
[lock lock];
if (video != nil) {
[selections addObject:video];
}
processed++;
[lock unlock];
if (processed == [assets count]) {
self.resolve(selections);
[indicatorView stopAnimating];
[overlayView removeFromSuperview];
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
return;
}
}];
} else {
[manager
requestImageDataForAsset:phAsset
options:options
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
UIImage *image = [UIImage imageWithData:imageData];
NSData *data = UIImageJPEGRepresentation(image, 1);
NSString *filePath = [self persistFile:data];
if (filePath == nil) {
self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
return;
}
[lock lock];
[selections addObject:[self createAttachmentResponse:filePath
withWidth:@(phAsset.pixelWidth)
withHeight:@(phAsset.pixelHeight)
withMime:@"image/jpeg"
withSize:[NSNumber numberWithUnsignedInteger:data.length]
withData:[[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null]
]];
processed++;
[lock unlock];
if (processed == [assets count]) {
self.resolve(selections);
[indicatorView stopAnimating];
[overlayView removeFromSuperview];
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
return;
}
}];
}
}
}];
} else {
PHAsset *phAsset = [assets objectAtIndex:0];
if (phAsset.mediaType == PHAssetMediaTypeVideo) {
[self showActivityIndicator:^(UIActivityIndicatorView *indicatorView, UIView *overlayView) {
[self getVideoAsset:phAsset completion:^(NSDictionary* video) {
if (video != nil) {
self.resolve(video);
} else {
self.reject(ERROR_CANNOT_PROCESS_VIDEO_KEY, ERROR_CANNOT_PROCESS_VIDEO_MSG, nil);
}
[indicatorView stopAnimating];
[overlayView removeFromSuperview];
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
}];
}];
} else {
[manager [manager
requestImageDataForAsset:asset requestImageDataForAsset:phAsset
options:options options:nil
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { resultHandler:^(NSData *imageData, NSString *dataUTI,
UIImage *image = [UIImage imageWithData:imageData]; UIImageOrientation orientation,
NSData *data = UIImageJPEGRepresentation(image, 1); NSDictionary *info) {
[self processSingleImagePick:[UIImage imageWithData:imageData] withViewController:imagePickerController];
NSString *filePath = [self persistFile:data];
if (filePath == nil) {
self.reject(ERROR_CANNOT_SAVE_IMAGE_KEY, ERROR_CANNOT_SAVE_IMAGE_MSG, nil);
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
return;
}
[images addObject:@{
@"path": filePath,
@"width": @(asset.pixelWidth),
@"height": @(asset.pixelHeight),
@"mime": @"image/jpeg",
@"size": [NSNumber numberWithUnsignedInteger:data.length],
@"data": [[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null],
}];
}]; }];
} }
self.resolve(images);
[imagePickerController dismissViewControllerAnimated:YES completion:nil];
} else {
[manager
requestImageDataForAsset:[assets objectAtIndex:0]
options:nil
resultHandler:^(NSData *imageData, NSString *dataUTI,
UIImageOrientation orientation,
NSDictionary *info) {
[self processSingleImagePick:[UIImage imageWithData:imageData] withViewController:imagePickerController];
}];
} }
} }
@ -262,14 +449,13 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
return; return;
} }
self.resolve(@{ self.resolve([self createAttachmentResponse:filePath
@"path": filePath, withWidth:@(image.size.width)
@"width": @(image.size.width), withHeight:@(image.size.height)
@"height": @(image.size.height), withMime:@"image/jpeg"
@"mime": @"image/jpeg", withSize:[NSNumber numberWithUnsignedInteger:data.length]
@"size": [NSNumber numberWithUnsignedInteger:data.length], withData:[[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null]]);
@"data": [[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null],
});
[viewController dismissViewControllerAnimated:YES completion:nil]; [viewController dismissViewControllerAnimated:YES completion:nil];
} }
} }
@ -352,14 +538,12 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
return; return;
} }
self.resolve(@{ self.resolve([self createAttachmentResponse:filePath
@"path": filePath, withWidth:@(resizedImage.size.width)
@"width": @(resizedImage.size.width), withHeight:@(resizedImage.size.height)
@"height": @(resizedImage.size.height), withMime:@"image/jpeg"
@"mime": @"image/jpeg", withSize:[NSNumber numberWithUnsignedInteger:data.length]
@"size": [NSNumber numberWithUnsignedInteger:data.length], withData:[[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null]]);
@"data": [[self.options objectForKey:@"includeBase64"] boolValue] ? [data base64EncodedStringWithOptions:0] : [NSNull null],
});
[controller dismissViewControllerAnimated:YES completion:nil]; [controller dismissViewControllerAnimated:YES completion:nil];
} }
@ -367,15 +551,8 @@ RCT_EXPORT_METHOD(openPicker:(NSDictionary *)options
// at the moment it is not possible to upload image by reading PHAsset // at the moment it is not possible to upload image by reading PHAsset
// we are saving image and saving it to the tmp location where we are allowed to access image later // we are saving image and saving it to the tmp location where we are allowed to access image later
- (NSString*) persistFile:(NSData*)data { - (NSString*) persistFile:(NSData*)data {
// create tmp directory
NSString *tmpDirFullPath = [self getTmpDirectory];
BOOL dirCreated = [[NSFileManager defaultManager] createDirectoryAtPath: tmpDirFullPath
withIntermediateDirectories:YES attributes:nil error:nil];
if (!dirCreated) {
return nil;
}
// create temp file // create temp file
NSString *tmpDirFullPath = [self getTmpDirectory];
NSString *filePath = [tmpDirFullPath stringByAppendingString:[[NSUUID UUID] UUIDString]]; NSString *filePath = [tmpDirFullPath stringByAppendingString:[[NSUUID UUID] UUIDString]];
filePath = [filePath stringByAppendingString:@".jpg"]; filePath = [filePath stringByAppendingString:@".jpg"];

View File

@ -1,6 +1,6 @@
{ {
"name": "react-native-image-crop-picker", "name": "react-native-image-crop-picker",
"version": "0.8.2", "version": "0.8.3",
"description": "Select single or multiple images, with croping option", "description": "Select single or multiple images, with croping option",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {