Skip to content

Commit

Permalink
Merge pull request #44 from ivpusic/camera
Browse files Browse the repository at this point in the history
[FEATURE] Camera support
  • Loading branch information
ivpusic committed Aug 11, 2016
2 parents 27decf1 + 79d3dab commit 88a0fba
Show file tree
Hide file tree
Showing 7 changed files with 353 additions and 113 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Import library
import ImagePicker from 'react-native-image-crop-picker';
```

#### Select from gallery

Call single image picker with cropping
```javascript
ImagePicker.openPicker({
Expand All @@ -40,6 +42,17 @@ ImagePicker.openPicker({
});
```

#### Select from camera
```javascript
ImagePicker.openCamera({
width: 300,
height: 400,
cropping: true
}).then(image => {
console.log(image);
});
```

#### Request Object

| Property | Type | Description |
Expand Down
245 changes: 190 additions & 55 deletions android/src/main/java/com/reactnative/picker/PickerModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Base64;
import android.Manifest;
import android.os.Environment;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Promise;
Expand All @@ -23,6 +25,10 @@
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;

import android.support.v4.app.ActivityCompat;
import android.content.pm.PackageManager;

import com.yalantis.ucrop.UCrop;

import java.io.File;
Expand All @@ -40,13 +46,18 @@
public class PickerModule extends ReactContextBaseJavaModule implements ActivityEventListener {

private static final int IMAGE_PICKER_REQUEST = 1;
private static final int CAMERA_PICKER_REQUEST = 2;
private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";

private static final String E_PICKER_CANCELLED_KEY = "picker_cancel";
private static final String E_PICKER_CANCELLED_MSG = "User cancelled image selection";

private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";
private static final String E_FAILED_TO_OPEN_CAMERA = "E_FALIED_TO_OPEN_CAMERA";
private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";
private static final String E_CAMERA_IS_NOT_AVAILABLE = "E_CAMERA_IS_NOT_AVAILABLE";
private static final String E_CANNOT_LAUNCH_CAMERA = "CANNOT_LAUNCH_CAMERA";
private static final String E_PERMISSIONS_MISSING = "E_PERMISSIONS_MISSING";

private Promise mPickerPromise;
private Activity activity;
Expand All @@ -56,33 +67,84 @@ public class PickerModule extends ReactContextBaseJavaModule implements Activity
private boolean includeBase64 = false;
private int width = 100;
private int height = 100;
private Boolean tmpImage;
private final ReactApplicationContext mReactContext;
private Uri mCameraCaptureURI;

public PickerModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
mReactContext = reactContext;
}

@Override
public String getName() {
return "ImageCropPicker";
}

private void setConfiguration(final ReadableMap options) {
multiple = options.hasKey("multiple") && options.getBoolean("multiple");
includeBase64 = options.hasKey("includeBase64") && options.getBoolean("includeBase64");
width = options.hasKey("width") ? options.getInt("width") : width;
height = options.hasKey("height") ? options.getInt("height") : height;
cropping = options.hasKey("cropping") ? options.getBoolean("cropping") : cropping;
}

@ReactMethod
public void openPicker(final ReadableMap options, final Promise promise) {
public void openCamera(final ReadableMap options, final Promise promise) {
int requestCode = CAMERA_PICKER_REQUEST;
Intent cameraIntent;

if (!isCameraAvailable()) {
promise.reject(E_CAMERA_IS_NOT_AVAILABLE, "Camera not available");
return;
}

activity = getCurrentActivity();

if (activity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
return;
}

multiple = options.hasKey("multiple") && options.getBoolean("multiple");
includeBase64 = options.hasKey("includeBase64") && options.getBoolean("includeBase64");
width = options.hasKey("width") ? options.getInt("width") : width;
height = options.hasKey("height") ? options.getInt("height") : height;
cropping = options.hasKey("cropping") ? options.getBoolean("cropping") : cropping;
if (!permissionsCheck(activity)) {
promise.reject(E_PERMISSIONS_MISSING, "Required permission missing");
return;
}

setConfiguration(options);
mPickerPromise = promise;

try {
cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
tmpImage = true;
// we create a tmp file to save the result
File imageFile = createNewFile(true);
mCameraCaptureURI = Uri.fromFile(imageFile);

cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCameraCaptureURI);
if (cameraIntent.resolveActivity(mReactContext.getPackageManager()) == null) {
promise.reject(E_CANNOT_LAUNCH_CAMERA, "Cannot launch camera");
return;
}

activity.startActivityForResult(cameraIntent, requestCode);
} catch (Exception e) {
mPickerPromise.reject(E_FAILED_TO_OPEN_CAMERA, e);
}

}

@ReactMethod
public void openPicker(final ReadableMap options, final Promise promise) {
activity = getCurrentActivity();

// Store the promise to resolve/reject when picker returns data
if (activity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
return;
}

setConfiguration(options);
mPickerPromise = promise;

try {
Expand All @@ -96,7 +158,6 @@ public void openPicker(final ReadableMap options, final Promise promise) {
activity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);
} catch (Exception e) {
mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);
mPickerPromise = null;
}
}

Expand Down Expand Up @@ -157,64 +218,138 @@ private WritableMap getImage(Uri uri, boolean resolvePath) {
return image;
}

@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == IMAGE_PICKER_REQUEST) {
if (mPickerPromise != null) {
if (resultCode == Activity.RESULT_CANCELED) {
mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
} else if (resultCode == Activity.RESULT_OK) {
if (multiple) {
ClipData clipData = data.getClipData();
WritableArray result = new WritableNativeArray();

// only one image selected
if (clipData == null) {
result.pushMap(getImage(data.getData(), true));
} else {
for (int i = 0; i < clipData.getItemCount(); i++) {
result.pushMap(getImage(clipData.getItemAt(i).getUri(), true));
}
}

mPickerPromise.resolve(result);
mPickerPromise = null;
} else {
Uri uri = data.getData();

if (cropping) {
UCrop.Options options = new UCrop.Options();
options.setCompressionFormat(Bitmap.CompressFormat.JPEG);

UCrop.of(uri, Uri.fromFile(new File(activity.getCacheDir(), UUID.randomUUID().toString() + ".jpg")))
.withMaxResultSize(width, height)
.withAspectRatio(width, height)
.withOptions(options)
.start(activity);
} else {
mPickerPromise.resolve(getImage(uri, true));
mPickerPromise = null;
}
public void startCropping(Uri uri) {
UCrop.Options options = new UCrop.Options();
options.setCompressionFormat(Bitmap.CompressFormat.JPEG);

UCrop.of(uri, Uri.fromFile(new File(activity.getCacheDir(), UUID.randomUUID().toString() + ".jpg")))
.withMaxResultSize(width, height)
.withAspectRatio(width, height)
.withOptions(options)
.start(activity);
}

public void imagePickerResult(final int requestCode, final int resultCode, final Intent data) {
if (mPickerPromise == null) {
return;
}

if (resultCode == Activity.RESULT_CANCELED) {
mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
} else if (resultCode == Activity.RESULT_OK) {
if (multiple) {
ClipData clipData = data.getClipData();
WritableArray result = new WritableNativeArray();

// only one image selected
if (clipData == null) {
result.pushMap(getImage(data.getData(), true));
} else {
for (int i = 0; i < clipData.getItemCount(); i++) {
result.pushMap(getImage(clipData.getItemAt(i).getUri(), true));
}
}
}
} else if (requestCode == UCrop.REQUEST_CROP) {
if (data != null) {
final Uri resultUri = UCrop.getOutput(data);
if (resultUri != null) {
mPickerPromise.resolve(getImage(resultUri, false));

mPickerPromise.resolve(result);
} else {
Uri uri = data.getData();

if (cropping) {
startCropping(uri);
} else {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "Cannot find image data");
mPickerPromise.resolve(getImage(uri, true));
}
}
}
}

public void cameraPickerResult(final int requestCode, final int resultCode, final Intent data) {
if (mPickerPromise == null) {
return;
}

if (resultCode == Activity.RESULT_CANCELED) {
mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
} else if (resultCode == Activity.RESULT_OK && mCameraCaptureURI != null) {
Uri uri = mCameraCaptureURI;

if (cropping) {
UCrop.Options options = new UCrop.Options();
options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
startCropping(uri);
} else {
mPickerPromise.resolve(getImage(uri, true));
}
}
}

public void croppingResult(final int requestCode, final int resultCode, final Intent data) {
if (data != null) {
final Uri resultUri = UCrop.getOutput(data);
if (resultUri != null) {
mPickerPromise.resolve(getImage(resultUri, false));
} else {
mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "Cannot find image data");
}
} else {
mPickerPromise.reject(E_PICKER_CANCELLED_KEY, E_PICKER_CANCELLED_MSG);
}
}

mPickerPromise = null;
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == IMAGE_PICKER_REQUEST) {
imagePickerResult(requestCode, resultCode, data);
} else if (requestCode == CAMERA_PICKER_REQUEST) {
cameraPickerResult(requestCode, resultCode, data);
} else if (requestCode == UCrop.REQUEST_CROP) {
croppingResult(requestCode, resultCode, data);
}
}

@Override
public void onNewIntent(Intent intent) {
}

private boolean permissionsCheck(Activity activity) {
int writePermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
int cameraPermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA);

if (writePermission != PackageManager.PERMISSION_GRANTED || cameraPermission != PackageManager.PERMISSION_GRANTED) {
String[] PERMISSIONS = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
};

ActivityCompat.requestPermissions(activity, PERMISSIONS, 1);
return false;
}

return true;
}

private boolean isCameraAvailable() {
return mReactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)
|| mReactContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
}

private File createNewFile(final boolean forcePictureDirectory) {
String filename = "image-" + UUID.randomUUID().toString() + ".jpg";
if (tmpImage && (!forcePictureDirectory)) {
return new File(mReactContext.getCacheDir(), filename);
} else {
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
File f = new File(path, filename);

try {
path.mkdirs();
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}

return f;
}
}
}
10 changes: 6 additions & 4 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
<uses-permission
android:name="android.permission.CAMERA"
android:maxSdkVersion="18" />

<application
android:name=".MainApplication"
Expand Down
Loading

0 comments on commit 88a0fba

Please sign in to comment.