diff --git a/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt b/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt index ae21e6b916..c2e4e6a509 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/AvatarRenderer.kt @@ -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 { 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() { + + override fun onDestroy() { + callback.onDestroy() + } + + override fun onResourceReady(resource: Drawable, transition: Transition?) { + callback.onDrawableUpdated(resource) + } + + override fun onLoadStarted(placeholder: Drawable?) { + callback.onDrawableUpdated(placeholder) + } + + override fun onLoadFailed(errorDrawable: Drawable?) { + callback.onDrawableUpdated(errorDrawable) + } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt index 35c8276cd8..2862765708 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeModule.kt @@ -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 { diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt index e1eaaecc6f..d27475f77b 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/MessageItemFactory.kt @@ -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 diff --git a/app/src/main/java/im/vector/riotredesign/features/markdown/EventHtmlRenderer.kt b/app/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt similarity index 86% rename from app/src/main/java/im/vector/riotredesign/features/markdown/EventHtmlRenderer.kt rename to app/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt index 206d4282be..dcbe28f11b 100644 --- a/app/src/main/java/im/vector/riotredesign/features/markdown/EventHtmlRenderer.kt +++ b/app/src/main/java/im/vector/riotredesign/features/html/EventHtmlRenderer.kt @@ -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, diff --git a/app/src/main/java/im/vector/riotredesign/features/html/PillDrawableFactory.kt b/app/src/main/java/im/vector/riotredesign/features/html/PillDrawableFactory.kt new file mode 100644 index 0000000000..05885491f1 --- /dev/null +++ b/app/src/main/java/im/vector/riotredesign/features/html/PillDrawableFactory.kt @@ -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) + + override fun onDestroy() { + weakChipDrawable.clear() + } + + override fun onDrawableUpdated(drawable: Drawable?) { + weakChipDrawable.get()?.apply { + chipIcon = drawable + setBounds(0, 0, intrinsicWidth, (intrinsicHeight / 1.5f).toInt()) + } + } + + } + + +} +