plugins
plugins copied to clipboard
[@nativescript/imagepicker] Returns `ImageAsset` with `content://` scheme that can't be loaded with `ImageSource.fromAsset`
Versions
@nativescript/core: 7.3.0
@nativescript/android: 7.0.1
@nativescript/imagepicker: 1.0.5
Problem
Using @nativescript/imagepicker in Android 10, the returned selected image has the following form when logged to console:
{
"_observers": {},
"_options": {
"keepAspectRatio": true,
"autoScaleFactor": true
},
"_android": "content://com.android.providers.media.documents/document/image%3A29"
When trying to load this ImageAsset into an ImageSource with ImageSource.fromAsset I get an Asset 'content://com.android.providers.media.documents/document/image%3A29' cannot be found. error.
Example code
let context = imagepicker.create({
mode: "single",
mediaType: ImagePickerMediaType.Image,
});
context
.authorize()
.then(() => {
return context.present();
})
.then((selection) => {
if (selection.length > 0) {
ImageSource.fromAsset(selection[0])
.then((imageSource) => {
console.log("ImageSource loaded:", imageSource);
})
.catch((err) => console.error("Error loading ImageSource:", err));
}
});
Is this a bug, or is there another way to get an ImageSource from the result of the imagepicker selection?
As a heads up the issue looks to be this line: https://github.com/NativeScript/plugins/blob/master/packages/imagepicker/index.android.ts#L198
I'm getting these Uris returned on API Level 30 devices which suggests this line is causing the problem. Manually setting this to true fixed the issue for API Level 30 devices but I can't be sure of the impact.
To workaround the problem in my project I've:
- copied
UriHelperinto my own code as an exported class. - Wrapped my old ImageSource.fromAsset in a try catch
- In the catch, if android, call
UriHelper._calculateFileUriand try again.
Looks something like this:
await picker.authorize();
const result = await picker.present();
// No images selected then return null
// Should return here
if (!result?.length) return null;
const path = ImagePaths.rawImagePath();
const asset: ImageAsset = result[0];
try {
const image = await ImageSource.fromAsset(asset);
image.saveToFile(path, IMAGE_FORMAT);
return path;
} catch (error) {
// This the new bit while waiting on #145
if (isAndroid) {
const url = asset?.android;
if (!url) throw error;
const uri = android.net.Uri.parse(url);
const rawUrl = UriHelper._calculateFileUri(uri);
if (!rawUrl) throw error;
const newAsset = new ImageAsset(rawUrl);
const image = await ImageSource.fromAsset(newAsset);
image.saveToFile(path, IMAGE_FORMAT);
return path;
}
throw error;
}
I'll run some more tests but this should be working
@triniwiz on a chromebook running API 29 (ASUS Chromebook C202SA), UriHelper seems to throw with:
java.lang.IllegalArgumentException: column '_data' does not exist. Available columns: []
I assume the reason being deprecation https://developer.android.com/reference/android/provider/MediaStore.MediaColumns#DATA
but I am not sure
Anything new about this?
I updated nativescript to version 7 in the hope that things will work better, but there are still problems
Edit: @jamescodesthings you're right,changing the conditions solves the problem. But is it the best solution @triniwiz ?
When can I expect to update and fix the bug?
Can you which version of core you are using?
I reproduce this bug with this versions :
"@nativescript/android": "8.0.0",
"@nativescript/core": "~7.1.0",
"@nativescript/imagepicker": "^1.0.5",
The workaround of @jamescodesthings works
Same here. I am using @jamescodesthings hack while waiting for a fix
ImageSource.fromAsset(selection[0]) .then((imageSource) => { console.log("ImageSource loaded:", imageSource); }) .catch((err) => console.error("Error loading ImageSource:", err));
Hi @jamescodesthings , do you have a working code example of the UriHelper? I am trying to fix an similair issue but not quite managing. Maybe its my lack of skill.. if you do, it would be really helpful for me.
Thank you in advanced!
In 2.0.0 you can now pass copyToAppFolder: 'myfolder' which should address this issue. You can then either use
ImageSource.fromAsset(selected.asset).then((img) => { /// do things })
or
ImageSource.fromAsset(selected.path).then((img) => { /// do things })
FYI you can also pass renameFileTo: 'mycoolfile' and it will rename your selection. If you choose multiple it appends the index like
mycoolfile-0.png
mycoolfile-1.png
etc