How to load a URI with "content://" prefix using Glide Android?
A

6

8

I'm trying to load a Contact photo with URI "content://com.android.contacts/contacts/295" by using Glide.

When I use

Glide.with(context).load(Uri.parse(contactPhoto).into(imageview)

Glide gives me a FileNotFoundException

java.io.FileNotFoundException: File does not exist; URI: content://com.android.contacts/contacts/264, calling user: android.uid.shared:10006, calling package is one of: [com.android.providers.contacts, com.android.contacts, com.android.providers.userdictionary]
        at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
        at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:689)
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1080)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:921)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:848)
        at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:21)
        at com.bumptech.glide.load.data.FileDescriptorLocalUriFetcher.loadResource(FileDescriptorLocalUriFetcher.java:14)
        at com.bumptech.glide.load.data.LocalUriFetcher.loadData(LocalUriFetcher.java:44)
        at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:83)
        at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53)
        at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170)
        at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
        at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
        at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
        at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)
        at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:52)

Obviously Glide tries to get the image from the wrong place.

I would appreciate if someone point me on how to load a photo with "content://" URIs.

Ascanius answered 3/4, 2015 at 19:45 Comment(1)
github.com/bumptech/glide/issues/394Energize
C
3

You need to create a custom loader that uses a ContentResolver. In Picasso for example this works because there is already a custom request handler that uses a ContentResolver.

I created one custom Glide loader for contacts for my internal use that you can take as reference.

Cleancut answered 12/4, 2016 at 18:26 Comment(4)
Thanks for your answer, I've already submitted a pull request to Glide that implements this feature natively, I think it is going to be merged soon. github.com/bumptech/glide/pull/1119Ascanius
@AhmedI.Khalil Could you please post an example?Rorke
github.com/bumptech/glide/issues/394 The feature request is now resolved and it is going to be released with v3.8.0 in Glide. You'll either have to wait for v3.8.0 or you can include the Glide source code (branch v3.0) as a dependency in your project and have a look at this sample that I've wrote. github.com/bumptech/glide/blob/3.0/samples/contacturi/src/main/…Ascanius
@AhmedI.Khalil, Yea, I've seen the sample and it didn't work, cuz I didn't know that it will be included only in v3.8.0 :( Thanks, man!Rorke
A
1

Seems that Glide doesn't handle content photos Uri automatically.

So I've solved my issue using an RxJava approach.

Here is a method that emits a bitmap (Please notice the scheduler as it is important to not lag the scrolling performance)

private Observable<Bitmap> _getConvertInputStreamToBitmapObservable(ContentResolver cr,
                                                                    Uri contactUri) {
    return Observable.create(new Observable.OnSubscribe<Bitmap>() {
        @Override
        public void call(Subscriber<? super Bitmap> subscriber) {
            InputStream inputStream =
                    ContactsContract.Contacts.openContactPhotoInputStream(cr, contactUri);
            if (inputStream != null) {
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                subscriber.onNext(bitmap);
            }
            subscriber.onCompleted();
        }
    }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}

And here is the client code that uses the method (Please notice the unsubscribing as it is important in recycling).

       if (holder.bitmapSubscription != null) {
            holder.bitmapSubscription.unsubscribe();
        }

        holder.bitmapSubscription = _getConvertInputStreamToBitmapObservable(context.getContentResolver(),
                contactUri)
                .subscribe(holder.userImg::setImageBitmap);
Ascanius answered 3/4, 2015 at 22:30 Comment(0)
G
0

You need to use a ContentResolver for this.

ContentResolver contextResolver = context.getContentResolver();
Uri uri = Uri.parse("content://com.android.contacts/contacts/295");
Bitmap thumbnail = null;
Cursor cursor = contentResolver.query(uri, new String[] {ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);

try {
    if (cursor.moveToFirst()) {
        final byte[] thumbnailBytes = cursor.getBlob(0);
        if (thumbnailBytes != null) {
            thumbnail = BitmapFactory.decodeByteArray(thumbnailBytes, 0, thumbnailBytes.length);
        }
    }
}
finally {
    cursor.close();
}

if (thumbnail != null) {
    imageView.setImageBitmap(thumbnail);
}

Try this. This should work.

Griffen answered 3/4, 2015 at 20:15 Comment(3)
Yeah I know that there is a method called openContactPhotoInputStream. But that would retrieve the photo in the main thread, and I'm calling the Glide code in an Adapter and want it to handle getting the Image in a background thread.Ascanius
I haven't used that method as it doesn't work below API 14. And I don't see the problem here; Glide cannot be faster than the native framework classes. Besides, Glide doesn't guarantee to handle any Uri automatically.Griffen
I get java.lang.IllegalArgumentException: Invalid column data15 which is related to public static final String PHOTO = DATA15. Using API 21.Beneficence
T
0
  Uri uri =   
   ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, new
   Long(contactsPojo.getId()));
          final Uri displayPhotoUri = Uri.withAppendedPath(uri,
                  ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
          Glide.with(context)
                  .load(displayPhotoUri)
                  .placeholder(R.mipmap.ic_launcher)
                  .error(R.mipmap.ic_launcher)
                  .fallback(R.mipmap.ic_launcher)
                  .diskCacheStrategy(DiskCacheStrategy.ALL)
                  .into(holder.image);
Thereabout answered 14/1, 2017 at 21:38 Comment(0)
H
0

I am use this code then working successfully

 Glide.with(context)
                    .load(uri)
                    .addListener(new RequestListener<Drawable>() {
                        @Override
                        public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                            Log.i(TAG, "onLoadFailed: ");
                            return false;
                        }

                        @Override
                        public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                            Log.i(TAG, "onResourceReady: ");
                            return false;
                        }
                    })
                    .into(holder.imgProfile);
Hautbois answered 14/9, 2019 at 12:17 Comment(0)
L
0

The fastest and easiest way is to first figure out the CONTACT_ID. Then you match this CONTACT_ID against the PHOTO_URI.

//first create a cursor
val photoCursor = contentResolver.query(
                                ContactsContract.Contacts.CONTENT_URI,
                                photoProjection,
                                ContactsContract.Contacts._ID + "=?",
                                arrayOf(contactId),
                                null
                            )
//where photoProjection is like so
        val photoProjection: Array<String> = arrayOf(
            ContactsContract.Contacts.Photo.PHOTO_URI,
            ContactsContract.Contacts._ID
        )

//now grab the PHOTO_URI, this will only exist if there is a photo
                            val photo = if (photoCursor?.moveToFirst() == true) {
                       photoCursor.getString(photoCursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI))
                            } else {
                                null
                            }

//In Glide now you can load the URI directly
        Glide.with(this)
            .load(uri)
            .apply(imageTransform)
            .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
            .into(image)
Lucia answered 3/8, 2020 at 23:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.