Html : continue work on Pills. Still need to find how to handle avatar drawable.

This commit is contained in:
ganfra 2019-02-26 19:35:47 +01:00
parent 41b06bca60
commit 63bf4355b9
5 changed files with 136 additions and 25 deletions

View File

@ -16,16 +16,21 @@
package im.vector.riotredesign.features.home
import android.content.Context
import android.graphics.drawable.Drawable
import android.widget.ImageView
import androidx.core.content.ContextCompat
import com.amulyakhare.textdrawable.TextDrawable
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.MatrixPatterns
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
import im.vector.riotredesign.core.glide.GlideApp
import im.vector.riotredesign.core.glide.GlideRequest
object AvatarRenderer {
@ -41,19 +46,52 @@ object AvatarRenderer {
if (name.isNullOrEmpty()) {
return
}
buildGlideRequest(imageView.context, name, avatarUrl).into(imageView)
}
fun load(context: Context, avatarUrl: String?, name: String?, callback: Callback) {
if (name.isNullOrEmpty()) {
return
}
buildGlideRequest(context, name, avatarUrl).into(CallbackTarget(callback))
}
private fun buildGlideRequest(context: Context, name: String, avatarUrl: String?): GlideRequest<Drawable> {
val resolvedUrl = Matrix.getInstance().currentSession.contentUrlResolver().resolveFullSize(avatarUrl)
val avatarColor = ContextCompat.getColor(imageView.context, R.color.pale_teal)
val avatarColor = ContextCompat.getColor(context, R.color.pale_teal)
val isNameUserId = MatrixPatterns.isUserId(name)
val firstLetterIndex = if (isNameUserId) 1 else 0
val firstLetter = name[firstLetterIndex].toString().toUpperCase()
val fallbackDrawable = TextDrawable.builder().buildRound(firstLetter, avatarColor)
GlideApp
.with(imageView)
return GlideApp
.with(context)
.load(resolvedUrl)
.placeholder(fallbackDrawable)
.apply(RequestOptions.circleCropTransform())
.into(imageView)
}
interface Callback {
fun onDrawableUpdated(drawable: Drawable?)
fun onDestroy()
}
private class CallbackTarget(private val callback: Callback) : SimpleTarget<Drawable>() {
override fun onDestroy() {
callback.onDestroy()
}
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
callback.onDrawableUpdated(resource)
}
override fun onLoadStarted(placeholder: Drawable?) {
callback.onDrawableUpdated(placeholder)
}
override fun onLoadFailed(errorDrawable: Drawable?) {
callback.onDrawableUpdated(errorDrawable)
}
}

View File

@ -16,25 +16,39 @@
package im.vector.riotredesign.features.home
import im.vector.matrix.android.api.Matrix
import im.vector.riotredesign.features.home.group.SelectedGroupStore
import im.vector.riotredesign.features.home.room.VisibleRoomStore
import im.vector.riotredesign.features.home.room.detail.timeline.*
import im.vector.riotredesign.features.home.room.detail.timeline.CallItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.DefaultItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.MessageItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomHistoryVisibilityItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomMemberItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomNameItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.RoomTopicItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.list.RoomSummaryComparator
import im.vector.riotredesign.features.home.room.list.RoomSummaryController
import im.vector.riotredesign.features.markdown.EventHtmlRenderer
import im.vector.riotredesign.features.html.EventHtmlRenderer
import org.koin.dsl.module.module
class HomeModule(homeActivity: HomeActivity) {
val definition = module(override = true) {
single {
Matrix.getInstance().currentSession
}
single {
TimelineDateFormatter(get())
}
single {
EventHtmlRenderer(homeActivity)
EventHtmlRenderer(homeActivity, get())
}
single {

View File

@ -29,7 +29,7 @@ import im.vector.riotredesign.core.epoxy.RiotEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.markdown.EventHtmlRenderer
import im.vector.riotredesign.features.html.EventHtmlRenderer
import im.vector.riotredesign.features.media.MediaContentRenderer
import me.gujun.android.span.span

View File

@ -16,14 +16,13 @@
*
*/
package im.vector.riotredesign.features.markdown
package im.vector.riotredesign.features.html
import android.content.Context
import android.text.style.ImageSpan
import com.google.android.material.chip.ChipDrawable
import im.vector.matrix.android.api.permalinks.PermalinkData
import im.vector.matrix.android.api.permalinks.PermalinkParser
import im.vector.riotredesign.R
import im.vector.matrix.android.api.session.Session
import org.commonmark.node.BlockQuote
import org.commonmark.node.HtmlBlock
import org.commonmark.node.HtmlInline
@ -50,10 +49,11 @@ import ru.noties.markwon.html.tag.SuperScriptHandler
import ru.noties.markwon.html.tag.UnderlineHandler
import java.util.Arrays.asList
class EventHtmlRenderer(private val context: Context) {
class EventHtmlRenderer(private val context: Context,
private val session: Session) {
private val markwon = Markwon.builder(context)
.usePlugin(MatrixPlugin.create(context))
.usePlugin(MatrixPlugin.create(context, session))
.build()
fun render(text: String): CharSequence {
@ -62,7 +62,8 @@ class EventHtmlRenderer(private val context: Context) {
}
private class MatrixPlugin private constructor(private val context: Context) : AbstractMarkwonPlugin() {
private class MatrixPlugin private constructor(private val context: Context,
private val session: Session) : AbstractMarkwonPlugin() {
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.htmlParser(MarkwonHtmlParserImpl.create())
@ -75,7 +76,7 @@ private class MatrixPlugin private constructor(private val context: Context) : A
ImageHandler.create())
.addHandler(
"a",
MxLinkHandler(context))
MxLinkHandler(context, session))
.addHandler(
"blockquote",
BlockquoteHandler())
@ -127,13 +128,13 @@ private class MatrixPlugin private constructor(private val context: Context) : A
companion object {
fun create(context: Context): MatrixPlugin {
return MatrixPlugin(context)
fun create(context: Context, session: Session): MatrixPlugin {
return MatrixPlugin(context, session)
}
}
}
private class MxLinkHandler(private val context: Context) : TagHandler() {
private class MxLinkHandler(private val context: Context, private val session: Session) : TagHandler() {
private val linkHandler = LinkHandler()
@ -143,12 +144,9 @@ private class MxLinkHandler(private val context: Context) : TagHandler() {
val permalinkData = PermalinkParser.parse(link)
when (permalinkData) {
is PermalinkData.UserLink -> {
val chipDrawable = ChipDrawable.createFromResource(context, R.xml.pill_view)
chipDrawable.setText(permalinkData.userId)
chipDrawable.textEndPadding = 8f
chipDrawable.textStartPadding = 8f
chipDrawable.setBounds(0, 0, chipDrawable.intrinsicWidth, (chipDrawable.intrinsicHeight / 1.5f).toInt())
val span = ImageSpan(chipDrawable)
val user = session.getUser(permalinkData.userId) ?: return
val drawable = PillDrawableFactory.create(context, permalinkData.userId, user)
val span = ImageSpan(drawable)
SpannableBuilder.setSpans(
visitor.builder(),
span,

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.html
import android.content.Context
import android.graphics.drawable.Drawable
import com.google.android.material.chip.ChipDrawable
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotredesign.R
import im.vector.riotredesign.features.home.AvatarRenderer
import java.lang.ref.WeakReference
object PillDrawableFactory {
fun create(context: Context, userId: String, user: User?): Drawable {
val chipDrawable = ChipDrawable.createFromResource(context, R.xml.pill_view).apply {
setText(user?.displayName ?: userId)
textEndPadding = 8f
textStartPadding = 8f
setBounds(0, 0, intrinsicWidth, (intrinsicHeight / 1.5f).toInt())
}
val avatarRendererCallback = AvatarRendererChipCallback(chipDrawable)
// TODO: need to work on getting drawable async
//AvatarRenderer.load(context, user?.avatarUrl, user?.displayName, avatarRendererCallback)
return chipDrawable
}
private class AvatarRendererChipCallback(chipDrawable: ChipDrawable) : AvatarRenderer.Callback {
private val weakChipDrawable = WeakReference<ChipDrawable>(chipDrawable)
override fun onDestroy() {
weakChipDrawable.clear()
}
override fun onDrawableUpdated(drawable: Drawable?) {
weakChipDrawable.get()?.apply {
chipIcon = drawable
setBounds(0, 0, intrinsicWidth, (intrinsicHeight / 1.5f).toInt())
}
}
}
}