Questions around Android Q and Glide have been asked before but I'm not able to put them all together, so I hope someone is willing to help.
I'm saving images, downloaded from the cloud(Firebase) or chosen from gallery or taken with the camera, to local storage. I used to save it in the app's folder and could add it to the gallery, but with Android Q that is not possible anymore. So I changed my app to use the MediaStore and ContentResolver to access files and save it. Files are saved in the Pictures
directory of MediaStore.Images.Media.EXTERNAL_CONTENT_URI
.
StorageHandler:
public Uri createUriForFile(String fileName, String parentId)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
Uri uriFromContentResolver = getUriFromContentResolver(fileName);
if(uriFromContentResolver != null)
{
return uriFromContentResolver;
}else
{
ContentResolver resolver = baseActivity.getApplicationContext().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, EXTERNAL_PICTURE_RELATIVE_PATH);
return resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
}
}else {
File pictureFile = new File( EXTERNAL_PICTURE_STORAGE_DIR.getAbsolutePath() + File.separator + parentId + File.separator + fileName );
checkAndCreatePictureDir(parentId);
return FileProvider.getUriForFile(baseActivity, baseActivity.getApplicationContext().getPackageName() + ".fileprovider", pictureFile);
}
}
public Uri getUriFromContentResolver(String fileName)
{
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
ContentResolver resolver = baseActivity.getApplicationContext().getContentResolver();
Cursor queryCursor = resolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.RELATIVE_PATH}, MediaStore.Images.Media.DISPLAY_NAME + "=? ",
new String[]{fileName}, null);
if (queryCursor != null && queryCursor.moveToFirst())
{
Uri content = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon().appendEncodedPath(queryCursor.getString(queryCursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH)))
.appendPath(queryCursor.getString(queryCursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME))).build();
return content;
} else
{
return null;
}
}else
{
return null;
}
It saves the file to the storage, it can be seen with the Device Explorer in Android Studio. It is also uploaded to the cloud and can be viewed from there(if it is a new file). But when I'm trying to load it into a ImageView
with Glide, it gives me an error: java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://
. And I can't seem to figure out why. It used to work and the URI should be correct as it is used on other places as well(uploading the file, saving the file, etc.) and it works. What is the problem with Glide/my code?
Full error:
E/GlideExecutor: Request threw uncaught throwable
java.lang.UnsupportedOperationException: Unknown or unsupported URL: content://media/external/images/media/Pictures/**AppName**/IMG20200112164646.jpg
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:151)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:705)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1687)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1503)
at android.content.ContentResolver.openInputStream(ContentResolver.java:1187)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResourceFromUri(StreamLocalUriFetcher.java:85)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:60)
at com.bumptech.glide.load.data.StreamLocalUriFetcher.loadResource(StreamLocalUriFetcher.java:15)
at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
at com.bumptech.glide.load.engine.SourceGenerator.startNext(SourceGenerator.java:62)
at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:302)
at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:272)
at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:233)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:446)
Your getUriFromContentResolver()
is wrong. Query for the _ID
, then use ContentUris.withAppendedId()
to assemble the Uri
to use.
For example, in this sample app from this book, I use:
suspend fun getLocalUri(filename: String): Uri? =
withContext(Dispatchers.IO) {
val resolver = context.contentResolver
resolver.query(collection, PROJECTION, QUERY, arrayOf(filename), null)
?.use { cursor ->
if (cursor.count > 0) {
cursor.moveToFirst()
return@withContext ContentUris.withAppendedId(
collection,
cursor.getLong(0)
)
}
}
null
}
where PROJECTION
and QUERY
are:
private val PROJECTION = arrayOf(MediaStore.Video.Media._ID)
private const val QUERY = MediaStore.Video.Media.DISPLAY_NAME + " = ?"
and where collection
is:
private val collection =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) MediaStore.Video.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL
) else MediaStore.Video.Media.EXTERNAL_CONTENT_URI
(in my case, I am querying videos; you would use MediaStore.Images
instead)
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments