Closed kihaki closed 3 months ago
I have just tested the same images on Coil 2.x and Jetpack Compose in an Android Native app and the images show fine, no DecodeException
- if that helps
If you set ExifOrientationPolicy.IGNORE
in ImageLoader.Builder
does it fix the issue? Setting that will force ImageLoader
to use BitmapFactory
internally instead of ImageDecoder
and this sounds like it could be an underlying issue with ImageDecoder unfortunately. What Android version are you testing on?
I suppose this is because https://github.com/coil-kt/coil/pull/1990, but I can't reproduce it.
If ContentProvider
provide a AssetFileDescriptor
that inner FileDescriptor
doesn't support lseek
, then we cannot share that AFD.
Literally we need to do a fallback for AFDs that doesn't support seek(i.e. fallback to not share, use URI directly)
val afd = metadata.assetFileDescriptor
val imageSource = try {
Os.lseek(afd.fileDescriptor, afd.startOffset, SEEK_SET)
ImageDecoder.createSource { metadata.assetFileDescriptor }
} catch (e: ErrnoException) {
ImageDecoder.createSource(options.context.contentResolver, metadata.uri.toAndroidUri())
}
See https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/graphics/java/android/graphics/ImageDecoder.java;drc=18311bd177e42e4a29ac81d4897ddfe6ded5b96c;l=377 IDK if this helps.
Actually ContentProvider
have no reason to provide a fd not support seek, fd that normally backed by file on disk always supports seek.
Edit: https://github.com/coil-kt/coil/pull/1990 should not lead to this. Error message is incomplete source but AFDs that doesn't support lseek will only lead to truncated header(i.e. malformed data) https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/graphics/java/android/graphics/ImageDecoder.java#727
IMO currently workaround could be forbid StaticImageDecoder
create sources from certain blacklist(com.google.android.apps.photos.cloudpicker) of ContentProvider
.
As mostly DocumentsProvider
is the common ContentProvider
and this should be the optimal path.
So I mean, If you can still reproduce that, you try ImageDecoder.createSource
from that URI, and decode that source, if you still got "Incomplete input", then we know it's either a ImageDecoder
internal bug or "Google Photos" bug and we need to fallback to BitmapFactory
for "Google Photo"'s ContentProvider
, if this always success(i.e. call ImageDecoder.createSource
and decode manually), then it must be caused by https://github.com/coil-kt/coil/pull/1990 and we need https://github.com/coil-kt/coil/issues/2434#issuecomment-2293577105 as solution.
@revonateB0T Thanks for taking a look! I don't think we can effectively maintain a blocklist for cases like this as it will very tough to maintain. Is there any way to detect if file descriptor supports lseek
? We can fall back to BitmapFactoryDecoder
if it doesn't support it.
@revonateB0T Thanks for taking a look! I don't think we can effectively maintain a blocklist for cases like this as it will very tough to maintain. Is there any way to detect if file descriptor supports
lseek
? We can fall back toBitmapFactoryDecoder
if it doesn't support it.
Yeah! just try lseek
on that fd.
val afd = metadata.assetFileDescriptor
val imageSource = try {
Os.lseek(afd.fileDescriptor, afd.startOffset, SEEK_SET)
ImageDecoder.createSource { metadata.assetFileDescriptor }
} catch (e: ErrnoException) {
ImageDecoder.createSource(options.context.contentResolver, metadata.uri.toAndroidUri())
}
I just got back to this and looks like it was solved in the meantime, so I will just say you guys are amazing
Describe the bug I implemented an expect/actual wrapper for the Android PhotoPicker and use it to pick photos which reside in the google cloud.
I am returning the Uri of the picked images as a String and load them in coil for compose multiplatform like the following:
This works sometimes, e.g. some of the photos picked are displayed fine, some of them produce the error below. I have not found a pattern to this yet, as all photos picked reside in the google cloud (the photo picker downloads them before returning them to my app). Subjectively it feels like about 40% success, 60% failure rate, although I have not measured this yet. To date I have never had this issue when developing a completely native Android App with Jetpack Compose and Coil (2.x.x)
I have granted persisted media access to all returned uris as described here.
The uris that work and those that don't work also have the same pattern, here is one that works:
content://media/picker/0/com.google.android.apps.photos.cloudpicker/media/05ad89e2-eefe-4cb4-b1fe-d725f0984415-1_all_48803
Here is one that produces the error below:
content://media/picker/0/com.google.android.apps.photos.cloudpicker/media/05ad89e2-eefe-4cb4-b1fe-d725f0984415-1_all_48822
The images that work and don't work are from the exact same source (iPhone photo capture HEIC, uploaded into the Google Fotos app and then used on an Android Phone, through the Google Photos cloud, probably converted on the way somewhere).
Logs/Screenshots
Version My coil version is
3.0.0-alpha09
, the artifacts areio.coil-kt.coil3:coil
,io.coil-kt.coil3:coil-compose
andio.coil-kt.coil3:coil-network-ktor2