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.

Sam Judd
  • 7,317
  • 1
  • 38
  • 38
Ahmed I. Khalil
  • 743
  • 6
  • 20

6 Answers6

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.

lujop
  • 13,504
  • 9
  • 62
  • 95
  • 2
    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. https://github.com/bumptech/glide/pull/1119 – Ahmed I. Khalil Apr 13 '16 at 08:58
  • @AhmedI.Khalil Could you please post an example? – Mussa May 13 '16 at 12:10
  • 2
    https://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. https://github.com/bumptech/glide/blob/3.0/samples/contacturi/src/main/java/com/bumptech/glide/samples/contacturi/MainActivity.java – Ahmed I. Khalil May 13 '16 at 15:56
  • @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! – Mussa May 13 '16 at 16:25
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);
Ahmed I. Khalil
  • 743
  • 6
  • 20
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.

Yash Sampat
  • 30,051
  • 12
  • 94
  • 120
  • 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. – Ahmed I. Khalil Apr 03 '15 at 20:38
  • 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. – Yash Sampat Apr 03 '15 at 20:41
  • I get `java.lang.IllegalArgumentException: Invalid column data15` which is related to `public static final String PHOTO = DATA15`. Using API 21. – Henrique de Sousa Apr 15 '16 at 13:11
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);
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);
Nidhi Savaliya
  • 162
  • 2
  • 9
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)
azwethinkweiz
  • 522
  • 4
  • 8