Skip to content

Instantly share code, notes, and snippets.

@lannyf77
Created March 26, 2020 19:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lannyf77/6376f59f432a00fd88f40425576db233 to your computer and use it in GitHub Desktop.
Save lannyf77/6376f59f432a00fd88f40425576db233 to your computer and use it in GitHub Desktop.
iconImageGetter for TextView
package com.oath.doubleplay.utils
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.text.Html.ImageGetter
import android.util.Log
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.Request
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SizeReadyCallback
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import com.oath.doubleplay.stream.view.holder.CheckTweetId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.lang.ref.WeakReference
class TextViewIconGetter(textView: TextView, iconSizeInDp: Int, val sourceId: String, val iconReadyListener: OnIconAvailableListener?) : ImageGetter {
private val textViewRef: WeakReference<TextView> = WeakReference(textView)
private val iconSize: Int = (iconSizeInDp * Resources.getSystem().displayMetrics.density).toInt()
/**
* interface for being notified that a new icon drawable is available.
* The onIconAvailable is called when icon drawable is ready
*/
interface OnIconAvailableListener {
val owner: WeakReference<CheckTweetId> // owner can check if the data is for it, abstract
/**
* @param drawable a drawable or null
* @param textViewRef the textView the drawable is with
*/
fun onImageAvailable(drawable: Drawable?, sourceId: String, textViewRef: WeakReference<TextView>)
}
override fun getDrawable(source: String): Drawable? {
return textViewRef.get()?.let { txtView ->
val drawable = BitmapDrawablePlaceholder(txtView, iconSize, sourceId, iconReadyListener)
CoroutineScope(Dispatchers.IO).launch {
val requestOptions = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.override(iconSize, iconSize) // resize does not respect aspect ratio
Glide.with(textViewRef.get()!!.context)
.asBitmap()
.apply(requestOptions)
.load(source)
.into(drawable)
}
drawable
}
}
private class BitmapDrawablePlaceholder internal constructor(textView: TextView,
private val iconSize: Int,
private val sourceId: String,
private val iconReadyListener: OnIconAvailableListener?
) : BitmapDrawable(textView.resources, Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888)), Target<Bitmap> {
val textViewRef: WeakReference<TextView> = WeakReference(textView)
private var drawable: Drawable? = null
override fun draw(canvas: Canvas) {
if (drawable != null) {
drawable!!.draw(canvas)
}
}
private fun onDrawableReady(drawable: Drawable) {
try {
iconReadyListener?.let { listener ->
this.drawable = drawable
drawable.setBounds(0, 0, iconSize, iconSize)
setBounds(0, 0, iconSize, iconSize)
listener.onImageAvailable(this.drawable, sourceId, textViewRef)
}
} catch (ex: Throwable) {
Log.e("+++", "+++ !!! exception in onDrawableReady($ex)", ex)
}
}
override fun onLoadStarted(placeholderDrawable: Drawable?) {
placeholderDrawable?.let { onDrawableReady(it) }
}
override fun onLoadFailed(errorDrawable: Drawable?) {
Log.d("+++", "+++ onLoadStarted(), Drawable:$errorDrawable")
errorDrawable?.let { onDrawableReady(it) }
}
override fun onResourceReady(bitmap: Bitmap, transition: Transition<in Bitmap>?) {
onDrawableReady(BitmapDrawable(textViewRef.get()?.resources ?: Resources.getSystem(), bitmap))
}
override fun onLoadCleared(placeholderDrawable: Drawable?) {
placeholderDrawable?.let { onDrawableReady(it) }
}
override fun getSize(cb: SizeReadyCallback) {
cb.onSizeReady(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
}
override fun removeCallback(cb: SizeReadyCallback) {}
var cachedRequest: Request? = null
override fun setRequest(request: Request?) {
cachedRequest = request
}
override fun getRequest(): Request? {
return request
}
override fun onStart() {}
override fun onStop() {}
override fun onDestroy() {}
}
init {
Log.i("+++", "+++ TextViewIconGetter(), this:$this")
}
}
/**
* when icon is ready, make sure this listener is for the owner of the tweet and update the textView
*/
class IconReadyListener(override val owner: WeakReference<CheckTweetId>) : TextViewIconGetter.OnIconAvailableListener {
override fun onImageAvailable(drawable: Drawable?, sourceId: String, textViewRef: WeakReference<TextView>) {
CoroutineScope(Dispatchers.Main).launch {
try {
owner.get()?.let { tweetOwner ->
if (tweetOwner.isSameTweet(sourceId)) {
textViewRef.get()?.let {
it.text = it.text
}
}
}
} catch (ex: Throwable) {
Log.d("+++", "+++ !!! exception in onImageAvailable($ex)", ex)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment