Open diegolmello opened 3 years ago
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).~
@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
@SudoPlz Hi! I can't absolutely agree with you because later piece of code https://github.com/ivpusic/react-native-image-crop-picker/blob/2b95b88f0b45025cccea0d39be582ff4cf311d1e/android/src/main/java/com/reactnative/ivpusic/imagepicker/PickerModule.java#L225-L233
conditionally removes WRITE_EXTERNAL_STORAGE
for android 11 π€ . Do you think it is still an issue if that code just refers to WRITE_EXTERNAL_STORAGE
permission?
@SudoPlz I released an app with version 0.36.4 + target API level 30 + WRITE_EXTERNAL_STORAGE
in AndroidManifest last week on Google Play. It's been 4 days and there's no issue reported yet. At least I can confirm image picking, resizing, and cropping works as expected on Samsung Galaxy Note 20 + Android 11 in production.
I can confirm that conditionally removing the WRITE_EXTERNAL_STORAGE
permission stops our app from crashing too.
Looks like that was all it took.
In our app things were a bit more complicated though, because an external dependency (instabug) specified a maxSdkVersion
to the WRITE_EXTERNAL_STORAGE
without us knowing about it (which resulted in weird behavior).
All good after all.
@PaperMonster @SudoPlz
Can you guys please confirm if the play store has accepted your app after reviewing it? Because they have been rejecting our app and the reason for it is Scope storage access.
Even though our app doesn't ask for MANAGE_EXTERNAL_STORAGE
permission they keep on rejecting our app saying that we are requesting MANAGE_EXTERNAL_STORAGE
permission or not following best practicing.
Please check the below screenshot where clearly I can't see MANAGE_EXTERNAL_STORAGE
permission as part of Manifest but WRITE_EXTERNAL_STORAGE
is there which is conditionally handled by react-native-image-crop-picker and permission will not ask for Android 11. So, I don't understand the issue here and I don't understand why they are rejecting the app.
@ivpusic Please help
Thank you.
@joy-betterhalf Yes, I can confirm mine. My app has WRITE_EXTERNAL_STORAGE
but doesn't have MANAGE_EXTERNAL_STORAGE
. In my case, WRITE_EXTERNAL_STORAGE
is also needed for @react-native-community/cameraroll
in Android < 10 but I had to write alternative native module for CameraRoll.save()
for Android >= 10.
Perhaps you need to make sure requestLegacyExternalStorage=true
is not in AndroidManifest.xml and check other dependencies if any of them uses forbidden API.
Same here, I can confirm mine too, the app has WRITE_EXTERNAL_STORAGE
but doesn't have MANAGE_EXTERNAL_STORAGE
.
We don't use requestLegacyExternalStorage
at all now, but I bet it wouldn't be a problem if we did.
As @PaperMonster said make sure you check your merged manifest, and see what your other dependencies add to the final manifest. (We actually did have 1 dependency that messed with the WRITE_EXTERNAL_STORAGE
dependency without our knowledge (instabug).
@PaperMonster @SudoPlz
Thank you guys for your quick response. I really appreciate your help. This information will help me solve my issue and hopefully Play store will accept our app. π€
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..
Thanks @mysport12, your fix is working for me!
patch-package + react-native-image-crop-picker+0.36.4.patch.txt = β€οΈ
Any update about this, it's still ocurring with 0.40.0 + react-native 0.72.4
Error code: E_NO_LIBRARY_PERMISSION
I created a PR that fix it https://github.com/ivpusic/react-native-image-crop-picker/pull/1973
Version
Tell us which versions you are using:
Platform
Tell us to which platform this issue is related
From Google Play inbox:
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