react-native-image-crop-picker
react-native-image-crop-picker copied to clipboard
Starting May 5th, you must let us know why your app requires broad storage access
Version
Tell us which versions you are using:
- react-native-image-crop-picker v0.31.1
- react-native v0.63.4
Platform
Tell us to which platform this issue is related
- Android
From Google Play inbox:
We've detected that your app contains the requestLegacyExternalStorage flag in the manifest file of 1 or more of your app bundles or APKs.
Developers with apps on devices running Android 11+ must use Scoped Storage to give users better access control over their device storage. To release your app on Android 11 or newer after May 5th, you must either:
Update your app to use more privacy friendly best practices, such as the Storage Access Framework or Media Store API Update your app to declare the All files access (MANAGE_EXTERNAL_STORAGE) permission in the manifest file, and complete the All files access permission declaration in Play Console from May 5th Remove the All files access permission from your app entirely For apps targeting Android 11, the requestLegacyExternalStorage flag will be ignored. You must use the All files access permission to retain broad access.
Apps requesting access to the All files access permission without a permitted use will be removed from Google Play, and you won't be able to publish updates.
https://support.google.com/googleplay/android-developer/answer/10467955
Do we have a workaround for that?
Thanks!
Love react-native-image-crop-picker? Please consider supporting our collective: π https://opencollective.com/react-native-image-crop-picker/donate
I got this message too and would like to know if anyone can come up with a solution
+1
I suspect that some people get that warning after using the suggested fix here https://github.com/ivpusic/react-native-image-crop-picker/issues/1449#issuecomment-724638956, not sure if it affects everyone though?
+1
I suspect that some people get that warning after using the suggested fix here #1449 (comment), not sure if it affects everyone though?
Yes, indeed. (for me)
I do research about it and the waring is applicable to case when you are targeting level 30 Android 11 I think. If you target level 29 it should be ok right now, bu in November 2021 it is required to use Api level 30 for existing app if you want to place them in google play store. And also in Api level 30 the flag requestLegacyExternalStorage stop working and you must use scope storage introduce in android 11 , right now this library is not ready for this change. Google play policy timeline https://developer.android.com/distribute/play-policies
Is there some deadline when the lib will be updated?
In case it helps anyone else, I switched to using https://github.com/rnmods/react-native-document-picker for now, it can be used as an Image/Video picker by passing in the correct types, e.g:
DocumentPicker.pickMultiple({
type: [DocumentPicker.types.images, DocumentPicker.types.video],
});
And doesn't require the use of requestLegacyExternalStorage
to pick images from the gallery/other apps
Have any plan add scope storage for android 11?
Any solution?
Any updates on this issue ?
Waiting for solution to this issue.
Waiting for solution to this issue.
Any updates? π€ @ivpusic I see that you started working on this on July 19th. Did you make any progress so far?
would be cool if somebody from the community has some time to finish support for android 11+
With the deadline for existing apps near, are there any updates on the matter? :) @ivpusic could you describe what exactly is left to handle for the Android 11 support to be complete?
What actually needs to be done, what are possible issues? I tested this library on android 11 devices while targeting SDK 30. My use cases are pretty simple though: taking a picture, upload to backend; take/pick an image, crop it, upload to backend. Had no issues with current state of a library. Am I missing something?
I think so like @Pyroboomka . They said need to update the app after May 5th for new app. Last week i tried publish dummy app that use library requires storage access include react-native-image-crop-picker
and Google approved my dummy app! You can check the app on this link, the app work perfectly fine. I didn't really understand the issue but i still worry about this issue and what happening on November.
I have been able to use the package as well without any issues. I am patching it though to update a number of the dependency versions and to resolve errors/warnings provided by Android Studio. Patch code in txt file below..
@fyfirman You'll need to bump targetSDK to 30 in November to continue updating your app. After doing it, if you currently have requestLegacyExternalStorage in manifest, it stops working on android 11 devices -> so we get some new issues. The ones I've personally found - you can no longer create files on sdcard freely (fixed by using cache directory instead); you can no longer freely rewrite files in downloads directory, if they are not created by your app (requires some renaming or just checking that files exists before writing them to disk). Nothing more came up.
5th May email was send to anyone, who had an requestLegacyExternalStorage in their manifest, made me google like a madman that day. Stumbled on https://www.reddit.com/r/androiddev/comments/mwaqn1/scoped_storage_recap/. Hope someone find it useful.
TL;DR: if stuff works as is, you don't need to worry.
Since November is only a couple of days ahead I decided it's too risky to keep hoping for an update before then and therefore I've combined react-native-image-picker and react-native-image-crop-picker
and implemented some sort of patchy solution with both of them.
Since react-native-image-picker
is compatible with targetSdkVersion >= 30 (and therefore Android 11) I use that to access storage and capture images/videos with the camera. For additional cropping of the selected or captured photos I'm then additionally using react-native-image-crop-picker
(since react-native-image-picker
does not provide that functionality).
In my opinion, this works pretty decently (for the time being), since I've managed to cover the entire workflow I had before. The production release with that update was just accepted and published to Google Play Store.
If anyone is interested in the corresponding code, let me know and I'm happy to share it here.
@KochMario i would like to see the code. Thx!
@KochMario i would like to see the code. Thx!
import React from 'react';
import { ImagePickerResponse, launchCamera, launchImageLibrary } from 'react-native-image-picker';
import ImagePicker, { Image, Options as ImagePickerOptions } from 'react-native-image-crop-picker';
import {
PermissionsAndroid,
Platform,
Pressable,
StyleSheet,
Text,
View,
} from 'react-native';
import { getSystemVersion } from 'react-native-device-info';
const ImagesLimit = 5;
const config: ImagePickerOptionss = {
// ... your options for react-native-image-crop-picker
width: undefined,
height: undefined,
cropping: true,
showsSelectedCount: true, // ios only
compressImageQuality: 0.8, // value from 0 to 1 -> on iOS values larger than 0.8 basically have no effect
compressImageMaxHeight: 2000,
compressImageMaxWidth: 2000,
};
type Props = {
width?: number;
height?: number;
onImageSelected: (selected) => void;
multiple?: boolean | number;
freeStyleCropEnabled?: boolean;
useFrontCamera?: boolean; // Whether to default to the front/'selfie' camera when opened.
compressImageMaxHeight?: number;
compressImageMaxWidth?: number;
};
const UploadPhotoModal = ({
width,
height,
freeStyleCropEnabled = false,
multiple = false,
useFrontCamera = false,
compressImageMaxHeight = 2000,
compressImageMaxWidth = 2000,
...props
}: Props) => {
const onError = (errorCode?, errorMessage?) => {
console.log('πΈπΈπΈ Image Picker Error => ', errorCode, errorMessage);
// ...
// (if desired) - handle cropping error, e.g. show toast, modal, etc.
};
const cropAndReturnAssets = async assets => {
const result: Image[] = [];
if (assets && Array.isArray(assets)) {
const openProps = {
...config,
freeStyleCropEnabled,
compressImageMaxHeight,
compressImageMaxWidth,
};
const cropImage = async image => {
try {
return await ImagePicker.openCropper({
path: image.uri as string,
...openProps,
});
} catch (e: any) {
console.log('Cropper error', e.message, e.code);
// if cropping was canceled return null and therefore "remove" this image from selection
// else return the uncropped image -> might be because of image compatibility issues, e.g. .png sometimes doesn't work
// with image cropper
if (e.code === 'E_PICKER_CANCELLED') {
return null;
}
return image;
}
};
let uneditedImages = 0;
try {
for (const image of assets) {
// for some reason the cropper can only handle jpg/jpeg images, therefore we push all other image types
// without opening the cropper
if (image.type === 'image/jpeg' || image.type === 'image/jpg') {
const cropped = await cropImage(image);
// only push if the image was edited and a valid result returned
if (cropped) {
result.push(cropped);
}
} else {
uneditedImages += 1;
result.push({ ...image, data: image.base64 });
}
}
} catch (e) {
console.log('Crop error: ', e);
onError();
}
const croppedImages = result.map(image => 'data:image/jpeg;base64,' + image.data);
if (croppedImages.length === 1) {
props.onImageSelected(croppedImages[0]);
} else {
props.onImageSelected(croppedImages);
}
if (uneditedImages > 0) {
// if desired - show information to the user the X of his selected images couldn't be processed and were therefore
// added in their original state
const message =
uneditedImages === 1
? getTranslation('ImagePicker.ImageCouldNotBeEdited')
: getTranslation('ImagePicker.ImagesCouldNotBeEdited', { amount: uneditedImages });
// ...
// logic for showing info message to the user
}
}
};
const handleResponse = async ({ assets, didCancel, errorCode, errorMessage }: ImagePickerResponse) => {
if (errorCode || errorMessage) {
onError(errorCode, errorMessage);
} else if (didCancel) {
// ...
// logic for canceling
} else {
await cropAndReturnAssets(assets);
}
};
const openImagePicker = async () => {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
);
const multipleValue =
// ios > 14.0 supports specifying the max amount
Platform.OS === 'ios' && getSystemVersion() > '14.0' && Number.isInteger(multiple)
? (multiple as number)
: 0;
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
launchImageLibrary(
{
// ... your options
mediaType: 'photo',
quality: 0.9,
selectionLimit: multiple ? multipleValue : 1,
includeBase64: true,
},
handleResponse,
);
} else {
// handle insufficient permissions, e.g. show modal or info message
}
};
const openCamera = async () => {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
launchCamera(
{
// ... your options
mediaType: 'photo',
quality: 0.9,
cameraType: useFrontCamera ? 'front' : 'back',
includeBase64: true,
saveToPhotos: false,
},
handleResponse,
);
} else {
// handle insufficient permissions, e.g. show modal or info message
}
};
return (
<View style={styles.container}>
<Pressable onPress={openCamera}>
<Text style={styles.text}>{getTranslation('UploadPhoto.TakePhoto')}</Text>
</Pressable>
<Pressable onPress={openImagePicker}>
<Text style={styles.text}>{getTranslation('UploadPhoto.ChooseFromLibrary')}</Text>
</Pressable>
</View>
);
};
export default UploadPhotoModal;
I've truncated some parts and reduced it somewhat so you get an idea without having too much of my custom logic in it. If using that code consider and keep in mind to customize it to your specific needs and purposes (i.e. I don't require video upload currently and for the time being I'm okay with only jpg's being compatible with cropping).
@KochMario thank you very much. Cheers!
@KochMario thanks
I simply upgraded the library from 0.35.1 to 0.36.4 and removed requestLegacyExternalStorage
from AndroidManifest.xml
. Now I can pick and crop an image on Android 11 devices without any error. Is this good enough? I'm considering trying suggestions in this thread but unsure what exactly makes the latest version incomplete for Android 11.
@PaperMonster did you test on Android 10 after your changes?
@PaperMonster did you test on Android 10 after your changes?
Yes I did. There was no problem.
Android 10 never had problem before and after the changes in target SDK and library version.
On Android 11, cropping fails without requestLegacyExternalStorage=true
. Upgrading to 0.36.4 fixed that.
@PaperMonster does that mean the issue is fixed on 0.36.4 ? Can the maintainer of this library shed some light? Did someone work on fixing this? Thanks π
~Ok to answer my question, this library is still using the deprecated WRITE_EXTERNAL_STORAGE
permission and thus won't work on any app that uses Android API 30 and above (which since we're on November, is required to upload a binary to the store).~
https://github.com/ivpusic/react-native-image-crop-picker/blob/master/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java#L200
@ivpusic can you please confirm? ~Also are there any plans of fixing this properly (which as far as I know, would be to support the Data Storage api)~
UPDATE: I was wrong, this library no longer asks for that permission.
Thanks