So, we are trying to use telephoto to render some images from internal storage.
Basically we were just using this with Coil, and it was working correctly :
AsyncImage(
modifier = Modifier.fillMaxSize(),
model = uri,
contentDescription = "MediaImage"
)
And now we use the same way :
ZoomableAsyncImage(
modifier = Modifier.fillMaxSize(),
model = uri,
contentDescription = "MediaImage"
)
The uri has this form : /data/user/0/appId/cache/.tmpy7ImGl.jpeg
After some investigation I found that this code is failing :
try {
decoder.value = factory.create(context, imageSource, imageOptions)
} catch (e: IOException) {
errorReporter.onImageLoadingFailed(e, imageSource)
}
Where the imageSource is coming from this method :
private fun ImageResult.toSubSamplingImageSource(imageLoader: ImageLoader): SubSamplingImageSource? {
val result = this
val requestData = result.request.data
val preview = (result.drawable as? BitmapDrawable)?.bitmap?.asImageBitmap()
if (result is SuccessResult && result.drawable is BitmapDrawable) {
// Prefer reading of images directly from files whenever possible because
// that is significantly faster than reading from their input streams.
val imageSource = when {
result.diskCacheKey != null -> {
val diskCache = imageLoader.diskCache!!
val cached = diskCache[result.diskCacheKey!!] ?: error("Coil returned a null image from disk cache")
SubSamplingImageSource.file(cached.data, preview)
}
result.dataSource.let { it == DataSource.DISK || it == DataSource.MEMORY_CACHE } -> when {
requestData is Uri -> SubSamplingImageSource.contentUri(requestData, preview)
requestData is String -> SubSamplingImageSource.contentUri(Uri.parse(requestData), preview)
result.request.context.isResourceId(requestData) -> SubSamplingImageSource.resource(requestData, preview)
else -> null
}
else -> null
}
if (imageSource != null) {
return imageSource
}
}
return null
}
So I guess we are missing some check on the RequestData.Uri so it can be used with SubSamplingImageSource.file()
instead.
Coil is doing some mapping internally like that :
internal class FileUriMapper : Mapper<Uri, File> {
override fun map(data: Uri, options: Options): File? {
if (!isApplicable(data)) return null
if (data.scheme == SCHEME_FILE) {
return data.path?.let(::File)
} else {
// If the scheme is not "file", it's null, representing a literal path on disk.
// Assume the entire input, regardless of any reserved characters, is valid.
return File(data.toString())
}
}
private fun isApplicable(data: Uri): Boolean {
return !isAssetUri(data) &&
data.scheme.let { it == null || it == SCHEME_FILE } &&
data.path.orEmpty().startsWith('/') && data.firstPathSegment != null
}
}
Let me know if this is something you want to fix or if I've to do the conversion on my side.
Thanks!