How to re-load a local manually cached Bitmap with Glide - android

I use the next code to manually add a local bitmap into Glide's cache.
Now the problem I have is that I don't know how to read it back into an ImageView.
To save it I use a key as the signature.
fun saveBitmap(context: Context, key: String, bitmap: Bitmap) {
val signature = ObjectKey(key)
GlideApp.with(context)
.asBitmap()
.signature(signature)
.load(bitmap)
.preload()
}
Not working
fun loadImage(key: String, target: ImageView) {
val signature = ObjectKey(key)
GlideApp.with(target.context)
.load(key)
.signature(signature)
.into(target)
}

Related

Return is not returning the value

So the return function of my view model gives no return where in it is expected to give a return of bitmap so it can be used in UI to set the image.
Code of View Model :
val bitmap : MutableLiveData<Bitmap> by lazy { MutableLiveData<Bitmap>() }
fun retrive(doc_name : String,uid: String){
viewModelScope.launch(Dispatchers.IO){
bitmap.postValue(repository.retrive(doc_name,uid))
}
}
Code of Repository:
var localfile = createTempFile("tempImage", null)
var bitmap :Bitmap? = null
override suspend fun retrive(doc_name:String,uid: String) : Bitmap?{
val storageRef = FirebaseStorage.getInstance().reference?.child("/image/8WEFQnomCEMtlaSkCIkrBgT7XeO2/download")
storageRef.getFile(localfile).addOnSuccessListener {
bitmap = BitmapFactory.decodeFile(localfile.absolutePath)
}
return bitmap
}
Code in Fragment inside on View Created part:
val obsover = Observer<Bitmap>{
image.setImageBitmap(it)
}
admin_viewmodel.bitmap.observe(viewLifecycleOwner,obsover)
So because I kept in my Repository function that bitmap can be null it opens the fragment with no image in image view
But if I keep the Bitmap to not be null(!!) the app crashes and gives the Null Pointer Exception error in the lines below:
Inside the repository code I shared above:
return bitmap!!
2.Inside the View Model code I shared above:
bitmap.postValue(repository.retrive(doc_name,uid))
What I think is its Unable to return because things are working on different threads.
Kindy help me solve this, Thanks.
Edit after Broot Reply code changes:
override suspend fun retrive(doc_name:String,uid: String) : Bitmap {
return suspendCoroutine { cont ->
val storageRef =
FirebaseStorage.getInstance().reference?.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
val localfile = createTempFile("tempImage", null)
storageRef.getFile(localfile).addOnSuccessListener {
val x = cont.resume(bitmap!!)
Log.d("checck", "$x")
}
}
}
Your original code was incorrect because it fires off an asynchronous function to the API and then returns immediately, before that asynchronous work is done and has fired its callback to update the bitmap property.
Your second code is wrong, because it tries to resume the continuation with the value of the property bitmap, which you have not updated with the value that was returned in the callback. Also, since you're just wanting a Bitmap from the cloud file, there's no reason to download it to a temporary file. You can work directly with the bytes. And there's no reason to use a property that I can see. bitmap can be a local variable.
Also, since you don't do anything in case of failure, your function would hang if there is a problem retrieving the data from Firebase. Below, I just throw the error, but you could do something different like returning null if you want.
I don't know what you're doing with those two parameters, but I left them. I leave it up to you to decide what your byte limit should be (I just used 5 million bytes). I don't remember the guaranteed minimum amount of available memory is for an Android app, and you might know that the file you're retrieving is below that value anyway.
override suspend fun retrive(doc_name: String, uid: String): Bitmap = suspendCoroutine { cont ->
val storageRef =
FirebaseStorage.getInstance().reference.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
storageRef.getBytes(5_000_000L).addOnSuccessListener { byteArray ->
val bitmap = BitmapFactory.decodeByteArray(byteArray)
cont.resume(bitmap)
}.addOnFailureListener {
cont.resumeWithException(it)
}
}
However: Firebase already comes with the await() extension suspend function so you don't have to use suspendCoroutine.
override suspend fun retrive(doc_name: String, uid: String): Bitmap {
val storageRef =
FirebaseStorage.getInstance().reference.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
val byteArray = storageRef.getBytes(5_000_000L).await()
return BitmapFactory.decodeByteArray(byteArray)
}
Since decoding a bitmap is kind of a heavy operation, I would do this in Dispatchers.Default:
override suspend fun retrive(doc_name: String, uid: String): Bitmap = withContext(Dispatchers.Default) {
val storageRef =
FirebaseStorage.getInstance().reference.child("/image/0XhL4jD4XCemk38rcRkIEjJMgjh2/Aadhar")
val byteArray = storageRef.getBytes(5_000_000L).await()
return BitmapFactory.decodeByteArray(byteArray)
}

How to ensure that glide uses the palette to obtain color and fill it for a period of time before loading a successful picture

I want to use palette to get a color and fill it before glide successfully loads the picture.
Just like bing.com or twitter.com, a color will cover the image before it is successfully loaded.
I use the following methods to achieve it:
Glide
.with(imageView.context)
.asBitmap()
.load(imageUrl)
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(e: GlideException?, model: Any, target: Target<Bitmap>, isFirstResource: Boolean) = false
override fun onResourceReady(resource: Bitmap, model: Any, target: Target<Bitmap>, dataSource: DataSource, isFirstResource: Boolean): Boolean {
Palette
.from(resource)
.generate(PaletteAsyncListener { palette ->
val darkVibrantSwatch = palette!!.darkVibrantSwatch
val dominantSwatch = palette.dominantSwatch
val lightVibrantSwatch = palette.lightVibrantSwatch
if (darkVibrantSwatch != null) {
imageView.setBackgroundColor(darkVibrantSwatch.rgb)
} else if (dominantSwatch != null) {
imageView.setBackgroundColor(dominantSwatch.rgb)
} else {
imageView.setBackgroundColor(lightVibrantSwatch!!.rgb)
}
})
return false
}
})
.into(imageView)
However, through the above code, I will load the image directly (imagview will display white before the image is loaded successfully), without filling it with solid color first.
I think maybe glide is loading too fast?
Is there any way to ensure that my picture is filled with solid color for a period of time, such as 500ms?
use Palette.from(resource!!.toBitmap) this will force and rap the resources to bitmap. but firstly you will need to use let on the resources variable so that resources doe's not give you issues

How to save image from URL to local and get it's Uri?

I have the following code -
verifyOtpViewModel.userData.observe(this, Observer {
if (it == null) return#Observer
if (it.profileImage != null) {
...
}
}
profileImage is my image URL.
I need an updated way, by 2020 standard to get the image bitmap from the URL and then get the URI from that bitmap.
All answers on this subject use the soon-to-be deprecated AsyncTask and I was hoping for a better solution, maybe a 3rd party library even.
Thanks!
First download the image if it's not already downloaded
GlideApp is the generated API:
Add a class with exact same name. Be sure to Rebuild after adding this code snippet:
you need to use kapt instead of annotactionProccesser on Kotlin.
#GlideModule
class MyAppGlideModule : AppGlideModule()
diskCacheStrategy(DiskCacheStrategy.NONE) don't let Glide cache this image.
Target.Size_Original keep the original size when you download the image from url.
GlideApp
.with(context)
.asBitmap()
.load(uri)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(object : CustomTarget<Bitmap>(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) {
override fun onLoadCleared(placeholder: Drawable?) {}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
//Save image file
saveFile(resource)
//Load into imageview if needed
}
}
})
Save the image file. It's recommended to use background threads for this.
val randomFilename = //generate a name based on your preferences
val file = File(saveDirectory, randomFilename)
val fos = FileOutputStream(file)
val bos = BufferedOutputStream(fos)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos)
bos.close()
Update your room database with the name of the file to retrieve that later.
#Query("UPDATE table_name SET image_filename = :fileName WHERE id = :id ")
fun updateImageFilename(id: Int, filename: String)

Load vector drawable resource multiple times with glide issue

I am using glide 4.6.1 to load a Resource (a vector drawable). It's always the same resource but sometimes does not load properly. Looks Emily Blunt image.
The code:
fun AppCompatImageView.loadCircleImage(resId: Int, width: Int, height: Int) {
val options = RequestOptions()
.fitCenter()
.override(width, height)
.circleCrop()
Glide.with(context)
.load(AppCompatResources.getDrawable(context, resId))
.apply(options)
.into(this)
}

Glide Module Loading url Images In Custom Sizes with paths and any uri handling

I have followed this tutorial where it is declared a custom Glide Module to load different image sizes from server depending on the ImageView size. I have also taken a look to this Glide wiki which explains the same.
But the implementation on the tutorial and the Glide wiki only works if the String you send to the Custom Module is a http/https url. How can I modify this Custom Module or create a new one in order to handle all other types (String, Uri, int, etc as Glide.load() does) and keep the functionality presented in the tutorial?
Instead of registering the new ModelLoader with append(), which handle new types of data, register it using prepend(), which handle subsets of existing data where you do want to fall back to Glide’s default behavior if your ModelLoader fails. So instead of creating the new Glide's input data (in the tutorial named as CustomImageSizeModelFutureStudio), tell Glide, in the case of a String, to check if you want to modify the String and create your url or let Glide do his work without modifying the String. Here is my implementation in Kotlin. In this case, if your input is "https://...." it will request your custom url. If your input is "content://..." your ModelLoader will fail because of the handles() method and Glide will do it's work.
The implementation of AppGlideModule:
#GlideModule
class MyGlideModule : AppGlideModule() {
override fun registerComponents(context: Context?, glide: Glide?, registry: Registry?) {
registry?.prepend(String::class.java, InputStream::class.java, CustomImageSizeUrlLoaderFactory())
}
}
The implementation of ModelLoaderFactory:
class CustomImageSizeUrlLoaderFactory : ModelLoaderFactory<String, InputStream> {
private val modelCache = ModelCache<String, GlideUrl>(500)
override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<String, InputStream> {
val modelLoader = multiFactory.build(GlideUrl::class.java, InputStream::class.java)
return CustomImageSizeUrlLoader(modelLoader, modelCache)
}
override fun teardown() {
}
}
The implementation of BaseGlideUrlLoader:
class CustomImageSizeUrlLoader(concreteLoader: ModelLoader<GlideUrl, InputStream>, modelCache: ModelCache<String, GlideUrl>?) : BaseGlideUrlLoader<String>(concreteLoader, modelCache) {
override fun getUrl(baseImageUrl: String, width: Int, height: Int, options: Options?): String {
return baseImageUrl + "?w=" + width + "&h=" + height;
}
override fun handles(model: String): Boolean {
return baseImageUrl.startsWith("http://")
|| baseImageUrl.startsWith("https://")
}
}
And call your Glide as you will normally do, not as the tutorial does.
To load image in different sizes, you can use glide's default method override
Check below code snippet for load image from different sizes.
GlideApp
.with(context)
.load(url)
.override(customwidth, customheight) // resizes the image to these dimensions (in pixel). resize does not respect aspect ratio
.into(imageViewResize);
If you want to maintain aspect ratio as well, you can use fitCenter() or centerCrop().

Categories

Resources