Skip to content

Commit

Permalink
Merge branch 'ivpusic:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
maksimko authored Apr 30, 2024
2 parents 3a3c22f + 4394930 commit 35bcd61
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 71 deletions.
45 changes: 22 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ iOS/Android image picker with support for camera, video, configurable compressio
<img width=200 title="iOS Crop Circular" src="https://github.com/ivpusic/react-native-image-crop-picker/blob/master/images/ios_circular_crop.png">
</p>

## Important note
## Important notes

If you are using react-native >= 0.60 use react-native-image-crop-picker version >= 0.25.0. Otherwise use version < 0.25.0.
1. If you are using react-native >= 0.60 use react-native-image-crop-picker version >= 0.25.0. Otherwise use version < 0.25.0.
2. If you want to use react-native-image-crop-picker version >= 0.39.0 you have to set your android compileSdkVersion to 33 or greater. Otherwise use react-native-image-crop-picker version < 0.39.0

## Usage

Expand Down Expand Up @@ -63,7 +64,7 @@ ImagePicker.openPicker({
**Android: The prop 'cropping' has been known to cause videos not to be displayed in the gallery on Android. Please do not set cropping to true when selecting videos.**


### Select from camera
### Select from camera

#### Image

Expand Down Expand Up @@ -150,7 +151,9 @@ ImagePicker.clean().then(() => {
| hideBottomControls (android only) | bool (default false) | Whether to display bottom controls |
| enableRotationGesture (android only) | bool (default false) | Whether to enable rotating the image by hand gesture |
| cropperChooseText (ios only)  |           string (default choose)        | Choose button text |
| cropperChooseColor (ios only) | string (default `#FFCC00`) | HEX format color for the Choose button. [Default color is controlled by TOCropViewController](https://github.com/TimOliver/TOCropViewController/blob/a942414508012b13102f776eb65dac655f31cabb/Objective-C/TOCropViewController/Views/TOCropToolbar.m#L444). |
| cropperCancelText (ios only) | string (default Cancel) | Cancel button text |
| cropperCancelColor (ios only) | string (default tint `iOS` color ) | HEX format color for the Cancel button. Default value is the default tint iOS color [controlled by TOCropViewController](https://github.com/TimOliver/TOCropViewController/blob/a942414508012b13102f776eb65dac655f31cabb/Objective-C/TOCropViewController/Views/TOCropToolbar.m#L433) |
| cropperRotateButtonsHidden (ios only)  |           bool (default false)        | Enable or disable cropper rotate buttons |


Expand Down Expand Up @@ -205,19 +208,29 @@ pod install

In Xcode open Info.plist and add string key `NSPhotoLibraryUsageDescription` with value that describes why you need access to user photos. More info here https://forums.developer.apple.com/thread/62229. Depending on what features you use, you also may need `NSCameraUsageDescription` and `NSMicrophoneUsageDescription` keys.

#### (Optional) Step 2 - To localizate the camera / gallery / cropper text buttons
#### (Optional) Step 2 - To localize the camera / gallery / cropper text buttons

- Open your Xcode project
- Go to your project settings by opening the project name on the Navigation (left side)
- Select your project in the project list
- Select your project in the project list
- Should be into the Info tab and add in Localizations the language your app was missing throughout the +
- Rebuild and you should now have your app camera and gallery with the classic ios text in the language you added.

### Android

- **VERY IMPORTANT** Add the following to your `build.gradle`'s repositories section. (android/build.gradle)
- **VERY IMPORTANT** Add the following to your `build.gradle`'s repositories section and change Android SDK version to 33. (android/build.gradle)

```gradle
buildscript {
ext {
buildToolsVersion = "31.0.0"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
...
}
}
allprojects {
repositories {
mavenLocal()
Expand Down Expand Up @@ -248,23 +261,6 @@ android {
}
```

- Use Android SDK >= 26 (android/app/build.gradle)

```gradle
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
...
defaultConfig {
...
targetSdkVersion 27
...
}
...
}
```

- Minimum Gradle version if you are using react-native-image-crop-picker >= 0.35.0

```
Expand All @@ -277,6 +273,9 @@ android {

Reference for more details https://github.com/ivpusic/react-native-image-crop-picker/issues/1406

- If you use SDK version >= 33, add following to `app/src/main/AndroidManifest.xml`
- `<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>`

- [Optional] If you want to use camera picker in your project, add following to `app/src/main/AndroidManifest.xml`
- `<uses-permission android:name="android.permission.CAMERA"/>`

Expand Down
2 changes: 1 addition & 1 deletion RNImageCropPicker.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ Pod::Spec.new do |s|
qb.exclude_files = "ios/QBImagePicker/QBImagePicker/QBImagePicker.h"
qb.resource_bundles = { "QBImagePicker" => "ios/QBImagePicker/QBImagePicker/*.{lproj,storyboard}" }
qb.requires_arc = true
qb.frameworks = "Photos"
qb.frameworks = "Photos", "PhotosUI"
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.Manifest;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
Expand All @@ -15,6 +16,7 @@
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.webkit.MimeTypeMap;

import androidx.core.app.ActivityCompat;
Expand All @@ -38,16 +40,20 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Callable;



class PickerModule extends ReactContextBaseJavaModule implements ActivityEventListener {

private static final int IMAGE_PICKER_REQUEST = 61110;
Expand Down Expand Up @@ -582,9 +588,74 @@ private String resolveRealPath(Activity activity, Uri uri, boolean isCamera) thr
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

String externalCacheDirPath = Uri.fromFile(activity.getExternalCacheDir()).getPath();
String externalFilesDirPath = Uri.fromFile(activity.getExternalFilesDir(null)).getPath();
String cacheDirPath = Uri.fromFile(activity.getCacheDir()).getPath();
String FilesDirPath = Uri.fromFile(activity.getFilesDir()).getPath();

if (!path.startsWith(externalCacheDirPath)
&& !path.startsWith(externalFilesDirPath)
&& !path.startsWith(cacheDirPath)
&& !path.startsWith(FilesDirPath)) {
File copiedFile = this.createExternalStoragePrivateFile(activity, uri);
path = RealPathUtil.getRealPathFromURI(activity, Uri.fromFile(copiedFile));
}
}

return path;
}

private File createExternalStoragePrivateFile(Context context, Uri uri) throws FileNotFoundException {
InputStream inputStream = context.getContentResolver().openInputStream(uri);

String extension = this.getExtension(context, uri);
File file = new File(context.getExternalCacheDir(), "/temp/" + System.currentTimeMillis() + "." + extension);
File parentFile = file.getParentFile();
if (parentFile != null) {
parentFile.mkdirs();
}

try {
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does not
// try to copy it in chunks). Note that if external storage is
// not currently mounted this will silently fail.
OutputStream outputStream = new FileOutputStream(file);
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
outputStream.write(data);
inputStream.close();
outputStream.close();
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("image-crop-picker", "Error writing " + file, e);
}

return file;
}

public String getExtension(Context context, Uri uri) {
String extension;

//Check uri format to avoid null
if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//If scheme is a content
final MimeTypeMap mime = MimeTypeMap.getSingleton();
extension = mime.getExtensionFromMimeType(context.getContentResolver().getType(uri));
} else {
//If scheme is a File
//This will replace white spaces with %20 and also other special characters. This will avoid returning null values on file name with spaces and special characters.
extension = MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(new File(uri.getPath())).toString());

}

return extension;
}

private BitmapFactory.Options validateImage(String path) throws Exception {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Expand Down Expand Up @@ -867,4 +938,4 @@ private static WritableMap getCroppedRectMap(Intent data) {

return map;
}
}
}
18 changes: 17 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,13 +255,29 @@ declare module "react-native-image-crop-picker" {
*/
cropperCancelText?: string;

/**
* Cancel button color. HEX-like string color.
*
* @example '#ff00ee'
* @platform iOS only
*/
cropperCancelColor?: string;

/**
* Choose button text.
*
* @default 'Choose'
*/
cropperChooseText?: string;

/**
* Choose button color. HEX-like string color.
*
* @example '#EE00DD'
* @platform iOS only
*/
cropperChooseColor?: string;

/**
* Enable or disable cropper rotate buttons.
*
Expand Down Expand Up @@ -380,7 +396,7 @@ declare module "react-native-image-crop-picker" {
/**
* Extracted exif data from image. Response format is platform specific.
*/
exif?: null | object;
exif?: any;

/**
* Selected image's localidentifier, used for PHAsset searching.
Expand Down
9 changes: 9 additions & 0 deletions ios/QBImagePicker/QBImagePicker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
790A850E1AE7D4D9008E2A80 /* QBSlomoIconView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QBSlomoIconView.m; sourceTree = "<group>"; };
7D92D893270E4FF700BFC37E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/QBImagePicker.strings; sourceTree = "<group>"; };
7D92D894270E500300BFC37E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/QBImagePicker.strings; sourceTree = "<group>"; };
82C8A8C927C370E300A3B0EB /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/QBImagePicker.strings; sourceTree = "<group>"; };
1F83428028819165002788FE /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/QBImagePicker.strings; sourceTree = "<group>"; };
1F83427F28819162002788FE /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/QBImagePicker.strings; sourceTree = "<group>"; };
AA3AD06D1ACF9A3A00BF523E /* QBVideoIconView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QBVideoIconView.h; sourceTree = "<group>"; };
AA3AD06E1ACF9A3A00BF523E /* QBVideoIconView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QBVideoIconView.m; sourceTree = "<group>"; };
AA3AD0711ACFA06700BF523E /* QBVideoIndicatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QBVideoIndicatorView.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -256,6 +259,9 @@
uk,
sv,
nb,
vi,
da,
fi,
);
mainGroup = AAA8FDF91ACDA079002A9710;
productRefGroup = AAA8FE041ACDA079002A9710 /* Products */;
Expand Down Expand Up @@ -336,6 +342,9 @@
7D92D893270E4FF700BFC37E /* uk */,
7D92D894270E500300BFC37E /* sv */,
6525CF3C27CFBCA8004F9AAE /* nb */,
82C8A8C927C370E300A3B0EB /* vi */,
1F83428028819165002788FE /* da */,
1F83427F28819162002788FE /* fi */,
);
name = QBImagePicker.strings;
sourceTree = "<group>";
Expand Down
1 change: 1 addition & 0 deletions ios/QBImagePicker/QBImagePicker/QBAlbumsViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ - (void)updateControlState
-(void)managePermissionAction:(id)sender
{
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTableInBundle(@"permission.title", @"QBImagePicker", self.imagePickerController.assetBundle, nil) message:nil preferredStyle:UIAlertControllerStyleActionSheet];
actionSheet.popoverPresentationController.sourceView = sender;

[actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTableInBundle(@"permission.cancel", @"QBImagePicker", self.imagePickerController.assetBundle, nil) style:UIAlertActionStyleCancel handler:nil]];

Expand Down
Loading

0 comments on commit 35bcd61

Please sign in to comment.