Reputation: 743
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.
Upvotes: 8
Views: 8679
Reputation: 76799
When data-binding, none of this works.
In order to catch that uncatchable FileNotFoundException
stemming from Glide (which may dearly spam the log) or to substitute the missing images with a default image, one has to make sure that the referenced image even exists, before passing the Uri
to Glide.
Let's assume column Contacts.PHOTO_THUMBNAIL_URI
as item.thumbnail
:
content://com.android.contacts/contacts/110/photo
Where the XML looks about like this:
<QuickContactBadge
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="center"
android:contentDescription="@{ item.name }"
app:imageUrl="@{ item.thumbnail }"/>
The default data-binding component's companion object:
companion object {
val LOG_TAG: String = BaseFragment::class.java.simpleName
/** DataBinding the QuickContactBadge. */
@BindingAdapter("imageUrl")
fun loadImage(imageView: ImageView, uri: Uri?) {
if (uri != null && uriWouldResolve(imageView.context, uri)) {
Glide.with(imageView.context).load(uri).into(imageView)
} else {
// TODO: optionally load default image.
}
}
/** Reduce Log Spam. */
private fun uriWouldResolve(context: Context, uri: Uri): Boolean {
var exists = false
try {
val ins: InputStream? = context.contentResolver.openInputStream(uri)
if (ins?.read()!! > 0) {exists = true}
ins.close()
} catch (e: FileNotFoundException) {
Log.w(LOG_TAG, "file not found: ${e.message}")
}
return exists
}
}
Upvotes: 0
Reputation: 602
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)
Upvotes: 0
Reputation: 179
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);
Upvotes: 0
Reputation: 21
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);
Upvotes: 0
Reputation: 13883
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.
Upvotes: 3
Reputation: 743
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);
Upvotes: 1
Reputation: 30611
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.
Upvotes: 0