mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-15 01:35:07 +08:00
Pills : finalize avatar retrieval
This commit is contained in:
parent
fffdf4b8c1
commit
ef3fb561e9
@ -20,11 +20,16 @@ import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.DrawableImageViewTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import im.vector.matrix.android.api.Matrix
|
||||
import im.vector.matrix.android.api.MatrixPatterns
|
||||
import im.vector.matrix.android.api.session.content.ContentUrlResolver
|
||||
import im.vector.matrix.android.api.session.room.model.RoomMember
|
||||
import im.vector.matrix.android.api.session.room.model.RoomSummary
|
||||
import im.vector.riotredesign.R
|
||||
@ -32,6 +37,9 @@ import im.vector.riotredesign.core.glide.GlideApp
|
||||
import im.vector.riotredesign.core.glide.GlideRequest
|
||||
import im.vector.riotredesign.core.glide.GlideRequests
|
||||
|
||||
/**
|
||||
* This helper centralise ways to retrieve avatar into ImageView or even generic Target<Drawable>
|
||||
*/
|
||||
object AvatarRenderer {
|
||||
|
||||
@UiThread
|
||||
@ -46,23 +54,53 @@ object AvatarRenderer {
|
||||
|
||||
@UiThread
|
||||
fun render(avatarUrl: String?, name: String?, imageView: ImageView) {
|
||||
render(imageView.context, GlideApp.with(imageView), avatarUrl, name, imageView.height, DrawableImageViewTarget(imageView))
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun render(context: Context,
|
||||
glideRequest: GlideRequests,
|
||||
avatarUrl: String?,
|
||||
name: String?,
|
||||
size: Int,
|
||||
target: Target<Drawable>) {
|
||||
if (name.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
val placeholder = buildPlaceholderDrawable(imageView.context, name)
|
||||
buildGlideRequest(GlideApp.with(imageView), avatarUrl)
|
||||
val placeholder = buildPlaceholderDrawable(context, name)
|
||||
buildGlideRequest(glideRequest, avatarUrl, size)
|
||||
.placeholder(placeholder)
|
||||
.into(imageView)
|
||||
.into(target)
|
||||
}
|
||||
|
||||
fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> {
|
||||
val resolvedUrl = Matrix.getInstance().currentSession.contentUrlResolver().resolveFullSize(avatarUrl)
|
||||
@WorkerThread
|
||||
fun getCachedOrPlaceholder(context: Context,
|
||||
glideRequest: GlideRequests,
|
||||
avatarUrl: String?,
|
||||
text: String,
|
||||
size: Int): Drawable {
|
||||
val future = buildGlideRequest(glideRequest, avatarUrl, size).onlyRetrieveFromCache(true).submit()
|
||||
return try {
|
||||
future.get()
|
||||
} catch (exception: Exception) {
|
||||
buildPlaceholderDrawable(context, text)
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE API *********************************************************************************
|
||||
|
||||
private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?, size: Int): GlideRequest<Drawable> {
|
||||
val resolvedUrl = Matrix.getInstance().currentSession
|
||||
.contentUrlResolver()
|
||||
.resolveThumbnail(avatarUrl, size, size, ContentUrlResolver.ThumbnailMethod.SCALE)
|
||||
|
||||
return glideRequest
|
||||
.load(resolvedUrl)
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
}
|
||||
|
||||
fun buildPlaceholderDrawable(context: Context, text: String): Drawable {
|
||||
private fun buildPlaceholderDrawable(context: Context, text: String): Drawable {
|
||||
val avatarColor = ContextCompat.getColor(context, R.color.pale_teal)
|
||||
return if (text.isEmpty()) {
|
||||
TextDrawable.builder().buildRound("", avatarColor)
|
||||
|
@ -48,11 +48,6 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
|
||||
findPillsAndProcess { it.bind(holder.messageView) }
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
findPillsAndProcess { it.unbind() }
|
||||
super.unbind(holder)
|
||||
}
|
||||
|
||||
private fun findPillsAndProcess(processBlock: (span: PillImageSpan) -> Unit) {
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
val pillImageSpans: Array<PillImageSpan>? = withContext(Dispatchers.IO) {
|
||||
|
@ -148,7 +148,7 @@ private class MxLinkHandler(private val glideRequests: GlideRequests,
|
||||
val permalinkData = PermalinkParser.parse(link)
|
||||
when (permalinkData) {
|
||||
is PermalinkData.UserLink -> {
|
||||
val user = session.getUser(permalinkData.userId) ?: return
|
||||
val user = session.getUser(permalinkData.userId)
|
||||
val span = PillImageSpan(glideRequests, context, permalinkData.userId, user)
|
||||
SpannableBuilder.setSpans(
|
||||
visitor.builder(),
|
||||
|
@ -22,7 +22,7 @@ import android.graphics.Paint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.style.ReplacementSpan
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.annotation.UiThread
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.google.android.material.chip.ChipDrawable
|
||||
@ -32,36 +32,33 @@ import im.vector.riotredesign.core.glide.GlideRequests
|
||||
import im.vector.riotredesign.features.home.AvatarRenderer
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* This span is able to replace a text by a [ChipDrawable]
|
||||
* It's needed to call [bind] method to start requesting avatar, otherwise only the placeholder icon will be displayed if not already cached.
|
||||
*/
|
||||
|
||||
private const val PILL_AVATAR_SIZE = 80
|
||||
|
||||
class PillImageSpan(private val glideRequests: GlideRequests,
|
||||
private val context: Context,
|
||||
private val userId: String,
|
||||
private val user: User?) : ReplacementSpan() {
|
||||
|
||||
private val pillDrawable = createChipDrawable(context, userId, user)
|
||||
private val displayName by lazy {
|
||||
if (user?.displayName.isNullOrEmpty()) userId else user?.displayName!!
|
||||
}
|
||||
|
||||
private val pillDrawable = createChipDrawable()
|
||||
private val target = PillImageSpanTarget(this)
|
||||
private var tv: WeakReference<TextView>? = null
|
||||
|
||||
@MainThread
|
||||
@UiThread
|
||||
fun bind(textView: TextView) {
|
||||
tv = WeakReference(textView)
|
||||
AvatarRenderer.buildGlideRequest(glideRequests, user?.avatarUrl).into(target)
|
||||
AvatarRenderer.render(context, glideRequests, user?.avatarUrl, displayName, PILL_AVATAR_SIZE, target)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun unbind() {
|
||||
glideRequests.clear(target)
|
||||
tv = null
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun updateAvatarDrawable(drawable: Drawable?) {
|
||||
pillDrawable.apply {
|
||||
chipIcon = drawable
|
||||
}
|
||||
tv?.get()?.apply {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
// ReplacementSpan *****************************************************************************
|
||||
|
||||
override fun getSize(paint: Paint, text: CharSequence,
|
||||
start: Int,
|
||||
@ -85,7 +82,6 @@ class PillImageSpan(private val glideRequests: GlideRequests,
|
||||
y: Int,
|
||||
bottom: Int,
|
||||
paint: Paint) {
|
||||
|
||||
canvas.save()
|
||||
val transY = bottom - pillDrawable.bounds.bottom
|
||||
canvas.translate(x, transY.toFloat())
|
||||
@ -93,37 +89,50 @@ class PillImageSpan(private val glideRequests: GlideRequests,
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
private fun createChipDrawable(context: Context, userId: String, user: User?): ChipDrawable {
|
||||
internal fun updateAvatarDrawable(drawable: Drawable?) {
|
||||
pillDrawable.apply {
|
||||
chipIcon = drawable
|
||||
}
|
||||
tv?.get()?.apply {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
// Private methods *****************************************************************************
|
||||
|
||||
private fun createChipDrawable(): ChipDrawable {
|
||||
val textPadding = context.resources.getDimension(R.dimen.pill_text_padding)
|
||||
val displayName = if (user?.displayName.isNullOrEmpty()) userId else user?.displayName!!
|
||||
return ChipDrawable.createFromResource(context, R.xml.pill_view).apply {
|
||||
setText(displayName)
|
||||
textEndPadding = textPadding
|
||||
textStartPadding = textPadding
|
||||
setChipMinHeightResource(R.dimen.pill_min_height)
|
||||
setChipIconSizeResource(R.dimen.pill_avatar_size)
|
||||
chipIcon = AvatarRenderer.buildPlaceholderDrawable(context, displayName)
|
||||
chipIcon = AvatarRenderer.getCachedOrPlaceholder(context, glideRequests, user?.avatarUrl, displayName, PILL_AVATAR_SIZE)
|
||||
setBounds(0, 0, intrinsicWidth, intrinsicHeight)
|
||||
}
|
||||
}
|
||||
|
||||
private class PillImageSpanTarget(pillImageSpan: PillImageSpan) : SimpleTarget<Drawable>() {
|
||||
}
|
||||
|
||||
private val pillImageSpan = WeakReference(pillImageSpan)
|
||||
/**
|
||||
* Glide target to handle avatar retrieval into [PillImageSpan].
|
||||
*/
|
||||
private class PillImageSpanTarget(pillImageSpan: PillImageSpan) : SimpleTarget<Drawable>() {
|
||||
|
||||
override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
|
||||
updateWith(drawable)
|
||||
}
|
||||
private val pillImageSpan = WeakReference(pillImageSpan)
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
updateWith(placeholder)
|
||||
}
|
||||
|
||||
private fun updateWith(drawable: Drawable?) {
|
||||
pillImageSpan.get()?.apply {
|
||||
this.updateAvatarDrawable(drawable)
|
||||
}
|
||||
}
|
||||
override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
|
||||
updateWith(drawable)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
updateWith(placeholder)
|
||||
}
|
||||
|
||||
private fun updateWith(drawable: Drawable?) {
|
||||
pillImageSpan.get()?.apply {
|
||||
updateAvatarDrawable(drawable)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user