Adjust colors for avatar and display names + start handling video in timeline

This commit is contained in:
ganfra 2019-04-11 19:19:52 +02:00
parent c38a601bcc
commit 9c9c09db2b
21 changed files with 188 additions and 100 deletions

View file

@ -31,6 +31,7 @@ class AutocompleteUserController : TypedEpoxyController<List<User>>() {
data.forEach { user ->
autocompleteUserItem {
id(user.userId)
userId(user.userId)
name(user.displayName)
avatarUrl(user.avatarUrl)
clickListener { _ ->

View file

@ -29,18 +29,15 @@ import im.vector.riotredesign.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_autocomplete_user)
abstract class AutocompleteUserItem : VectorEpoxyModel<AutocompleteUserItem.Holder>() {
@EpoxyAttribute
var name: String? = null
@EpoxyAttribute
var avatarUrl: String? = null
@EpoxyAttribute
var clickListener: View.OnClickListener? = null
@EpoxyAttribute var name: String? = null
@EpoxyAttribute var userId: String = ""
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
holder.view.setOnClickListener(clickListener)
holder.nameView.text = name
AvatarRenderer.render(avatarUrl, name, holder.avatarImageView)
AvatarRenderer.render(avatarUrl, userId, name, holder.avatarImageView)
}
class Holder : VectorEpoxyHolder() {

View file

@ -29,7 +29,6 @@ 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
import im.vector.riotredesign.core.glide.GlideApp
@ -44,39 +43,41 @@ object AvatarRenderer {
private const val THUMBNAIL_SIZE = 250
@UiThread
fun render(roomMember: RoomMember, imageView: ImageView) {
render(roomMember.avatarUrl, roomMember.displayName, imageView)
}
private val AVATAR_COLOR_LIST = listOf(
R.color.avatar_color_1,
R.color.avatar_color_2,
R.color.avatar_color_3
)
@UiThread
fun render(roomSummary: RoomSummary, imageView: ImageView) {
render(roomSummary.avatarUrl, roomSummary.displayName, imageView)
render(roomSummary.avatarUrl, roomSummary.roomId, roomSummary.displayName, imageView)
}
@UiThread
fun render(avatarUrl: String?, name: String?, imageView: ImageView) {
render(imageView.context, GlideApp.with(imageView), avatarUrl, name, DrawableImageViewTarget(imageView))
fun render(avatarUrl: String?, identifier: String, name: String?, imageView: ImageView) {
render(imageView.context, GlideApp.with(imageView), avatarUrl, identifier, name, DrawableImageViewTarget(imageView))
}
@UiThread
fun render(context: Context,
glideRequest: GlideRequests,
avatarUrl: String?,
identifier: String,
name: String?,
target: Target<Drawable>) {
if (name.isNullOrEmpty()) {
return
}
val placeholder = getPlaceholderDrawable(context, name)
val placeholder = getPlaceholderDrawable(context, identifier, name)
buildGlideRequest(glideRequest, avatarUrl)
.placeholder(placeholder)
.into(target)
}
@AnyThread
fun getPlaceholderDrawable(context: Context, text: String): Drawable {
val avatarColor = ContextCompat.getColor(context, R.color.pale_teal)
fun getPlaceholderDrawable(context: Context, identifier: String, text: String): Drawable {
val avatarColor = ContextCompat.getColor(context, getAvatarColor(identifier))
return if (text.isEmpty()) {
TextDrawable.builder().buildRound("", avatarColor)
} else {
@ -87,9 +88,21 @@ object AvatarRenderer {
}
}
// PRIVATE API *********************************************************************************
private fun getAvatarColor(text: String? = null): Int {
var colorIndex: Long = 0
if (!text.isNullOrEmpty()) {
var sum: Long = 0
for (i in 0 until text.length) {
sum += text[i].toLong()
}
colorIndex = sum % AVATAR_COLOR_LIST.size
}
return AVATAR_COLOR_LIST[colorIndex.toInt()]
}
private fun buildGlideRequest(glideRequest: GlideRequests, avatarUrl: String?): GlideRequest<Drawable> {
val resolvedUrl = Matrix.getInstance().currentSession!!.contentUrlResolver()
.resolveThumbnail(avatarUrl, THUMBNAIL_SIZE, THUMBNAIL_SIZE, ContentUrlResolver.ThumbnailMethod.SCALE)

View file

@ -35,6 +35,7 @@ class GroupSummaryController : TypedEpoxyController<GroupListViewState>() {
val isSelected = groupSummary.groupId == selected?.groupId
groupSummaryItem {
id(groupSummary.groupId)
groupId(groupSummary.groupId)
groupName(groupSummary.displayName)
selected(isSelected)
avatarUrl(groupSummary.avatarUrl)

View file

@ -29,6 +29,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
abstract class GroupSummaryItem : VectorEpoxyModel<GroupSummaryItem.Holder>() {
@EpoxyAttribute lateinit var groupName: CharSequence
@EpoxyAttribute lateinit var groupId: String
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute var listener: (() -> Unit)? = null
@ -37,7 +38,7 @@ abstract class GroupSummaryItem : VectorEpoxyModel<GroupSummaryItem.Holder>() {
super.bind(holder)
holder.rootView.isSelected = selected
holder.rootView.setOnClickListener { listener?.invoke() }
AvatarRenderer.render(avatarUrl, groupName.toString(), holder.avatarImageView)
AvatarRenderer.render(avatarUrl, groupId, groupName.toString(), holder.avatarImageView)
}
class Holder : VectorEpoxyHolder() {

View file

@ -37,6 +37,8 @@ import com.otaliastudios.autocomplete.Autocomplete
import com.otaliastudios.autocomplete.AutocompleteCallback
import com.otaliastudios.autocomplete.CharPolicy
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.user.model.User
import im.vector.riotredesign.R
@ -65,7 +67,7 @@ import im.vector.riotredesign.features.home.room.detail.composer.TextComposerVie
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.helper.EndlessRecyclerViewScrollListener
import im.vector.riotredesign.features.html.PillImageSpan
import im.vector.riotredesign.features.media.MediaContentRenderer
import im.vector.riotredesign.features.media.ImageContentRenderer
import im.vector.riotredesign.features.media.MediaViewerActivity
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.fragment_room_detail.*
@ -379,11 +381,16 @@ class RoomDetailFragment : VectorBaseFragment(), TimelineEventController.Callbac
roomDetailViewModel.process(RoomDetailActions.EventDisplayed(event))
}
override fun onMediaClicked(mediaData: MediaContentRenderer.Data, view: View) {
override fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View) {
val intent = MediaViewerActivity.newIntent(vectorBaseActivity, mediaData)
startActivity(intent)
}
override fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: ImageContentRenderer.Data, view: View) {
//TODO handle
}
// AutocompleteUserPresenter.Callback
override fun onQueryUsers(query: CharSequence?) {

View file

@ -25,15 +25,21 @@ import androidx.recyclerview.widget.RecyclerView
import com.airbnb.epoxy.EpoxyController
import com.airbnb.epoxy.EpoxyModel
import com.airbnb.epoxy.VisibilityState
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.LoadingItemModel_
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.*
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineAsyncHelper
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.detail.timeline.helper.nextDisplayableEvent
import im.vector.riotredesign.features.home.room.detail.timeline.item.DaySeparatorItem_
import im.vector.riotredesign.features.media.MediaContentRenderer
import im.vector.riotredesign.features.media.ImageContentRenderer
class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
private val timelineItemFactory: TimelineItemFactory,
@ -44,7 +50,8 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
interface Callback {
fun onEventVisible(event: TimelineEvent)
fun onUrlClicked(url: String)
fun onMediaClicked(mediaData: MediaContentRenderer.Data, view: View)
fun onImageMessageClicked(messageImageContent: MessageImageContent, mediaData: ImageContentRenderer.Data, view: View)
fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: ImageContentRenderer.Data, view: View)
}
private val modelCache = arrayListOf<List<EpoxyModel<*>>>()

View file

@ -28,7 +28,7 @@ import im.vector.matrix.android.api.session.room.model.message.MessageEmoteConte
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
@ -40,13 +40,13 @@ import im.vector.riotredesign.features.home.room.detail.timeline.helper.Timeline
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.DefaultItem_
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageItem_
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageVideoItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageImageVideoItem_
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageTextItem_
import im.vector.riotredesign.features.html.EventHtmlRenderer
import im.vector.riotredesign.features.media.MediaContentRenderer
import im.vector.riotredesign.features.media.ImageContentRenderer
import me.gujun.android.span.span
class MessageItemFactory(private val colorProvider: ColorProvider,
@ -79,15 +79,22 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
val avatarUrl = roomMember?.avatarUrl
val memberName = roomMember?.displayName ?: event.root.sender ?: ""
val formattedMemberName = span(memberName) {
textColor = colorProvider.getColor(colorIndexForSender(memberName))
textColor = colorProvider.getColor(getColorFor(event.root.sender ?: ""))
}
val informationData = MessageInformationData(time, avatarUrl, formattedMemberName, showInformation)
val informationData = MessageInformationData(eventId = eventId,
senderId = event.root.sender ?: "",
sendState = event.sendState,
time = time,
avatarUrl = avatarUrl,
memberName = formattedMemberName,
showInformation = showInformation)
return when (messageContent) {
is MessageEmoteContent -> buildEmoteMessageItem(messageContent, informationData, callback)
is MessageTextContent -> buildTextMessageItem(event.sendState, messageContent, informationData, callback)
is MessageImageContent -> buildImageMessageItem(eventId, messageContent, informationData, callback)
is MessageTextContent -> buildTextMessageItem(messageContent, informationData, callback)
is MessageImageContent -> buildImageMessageItem(messageContent, informationData, callback)
is MessageNoticeContent -> buildNoticeMessageItem(messageContent, informationData, callback)
is MessageVideoContent -> buildVideoMessageItem(messageContent, informationData, callback)
else -> buildNotHandledMessageItem(messageContent)
}
}
@ -97,31 +104,49 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return DefaultItem_().text(text)
}
private fun buildImageMessageItem(eventId: String,
messageContent: MessageImageContent,
private fun buildImageMessageItem(messageContent: MessageImageContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageImageItem? {
callback: TimelineEventController.Callback?): MessageImageVideoItem? {
val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val data = MediaContentRenderer.Data(
messageContent.body,
val data = ImageContentRenderer.Data(
filename = messageContent.body,
url = messageContent.url,
height = messageContent.info?.height,
maxHeight = maxHeight,
width = messageContent.info?.width,
maxWidth = maxWidth,
rotation = messageContent.info?.rotation,
orientation = messageContent.info?.orientation
orientation = messageContent.info?.orientation,
rotation = messageContent.info?.rotation
)
return MessageImageItem_()
.eventId(eventId)
return MessageImageVideoItem_()
.playable(messageContent.info?.mimeType == "image/gif")
.informationData(informationData)
.mediaData(data)
.clickListener { view -> callback?.onMediaClicked(data, view) }
.clickListener { view -> callback?.onImageMessageClicked(messageContent, data, view) }
}
private fun buildTextMessageItem(sendState: SendState,
messageContent: MessageTextContent,
private fun buildVideoMessageItem(messageContent: MessageVideoContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageImageVideoItem? {
val (maxWidth, maxHeight) = timelineMediaSizeProvider.getMaxSize()
val data = ImageContentRenderer.Data(
filename = messageContent.body,
url = messageContent.info?.thumbnailUrl,
height = messageContent.info?.height,
maxHeight = maxHeight,
width = messageContent.info?.width,
maxWidth = maxWidth
)
return MessageImageVideoItem_()
.playable(true)
.informationData(informationData)
.mediaData(data)
.clickListener { view -> callback?.onVideoMessageClicked(messageContent, data, view) }
}
private fun buildTextMessageItem(messageContent: MessageTextContent,
informationData: MessageInformationData,
callback: TimelineEventController.Callback?): MessageTextItem? {
@ -129,15 +154,7 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
htmlRenderer.render(it)
} ?: messageContent.body
val textColor = if (sendState.isSent()) {
R.color.dark_grey
} else {
R.color.brown_grey
}
val formattedBody = span(bodyToUse) {
this.textColor = colorProvider.getColor(textColor)
}
val linkifiedBody = linkifyBody(formattedBody, callback)
val linkifiedBody = linkifyBody(bodyToUse, callback)
return MessageTextItem_()
.message(linkifiedBody)
.informationData(informationData)
@ -184,8 +201,9 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return spannable
}
//Based on riot-web implementation
@ColorRes
private fun colorIndexForSender(sender: String): Int {
private fun getColorFor(sender: String): Int {
var hash = 0
var i = 0
var chr: Char
@ -210,6 +228,4 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
else -> R.color.username_8
}
}
}

View file

@ -25,7 +25,7 @@ import android.widget.TextView
import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.session.content.ContentUploadStateTracker
import im.vector.riotredesign.R
import im.vector.riotredesign.features.media.MediaContentRenderer
import im.vector.riotredesign.features.media.ImageContentRenderer
import java.io.File
object ContentUploadStateTrackerBinder {
@ -33,7 +33,7 @@ object ContentUploadStateTrackerBinder {
private val updateListeners = mutableMapOf<String, ContentUploadStateTracker.UpdateListener>()
fun bind(eventId: String,
mediaData: MediaContentRenderer.Data,
mediaData: ImageContentRenderer.Data,
progressLayout: ViewGroup) {
Matrix.getInstance().currentSession?.also { session ->
@ -56,7 +56,7 @@ object ContentUploadStateTrackerBinder {
}
private class ContentMediaProgressUpdater(private val progressLayout: ViewGroup,
private val mediaData: MediaContentRenderer.Data) : ContentUploadStateTracker.UpdateListener {
private val mediaData: ImageContentRenderer.Data) : ContentUploadStateTracker.UpdateListener {
override fun onUpdate(state: ContentUploadStateTracker.State) {
when (state) {

View file

@ -35,7 +35,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>()
holder.timeView.visibility = View.VISIBLE
holder.timeView.text = informationData.time
holder.memberNameView.text = informationData.memberName
AvatarRenderer.render(informationData.avatarUrl, informationData.memberName?.toString(), holder.avatarImageView)
AvatarRenderer.render(informationData.avatarUrl, informationData.senderId, informationData.memberName?.toString(), holder.avatarImageView)
} else {
holder.avatarImageView.visibility = View.GONE
holder.memberNameView.visibility = View.GONE
@ -43,6 +43,11 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>()
}
}
protected fun View.renderSendState() {
isClickable = informationData.sendState.isSent()
alpha = if (informationData.sendState.isSent()) 1f else 0.5f
}
abstract class Holder : VectorEpoxyHolder() {
abstract val avatarImageView: ImageView
abstract val memberNameView: TextView

View file

@ -24,27 +24,27 @@ import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotredesign.R
import im.vector.riotredesign.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.riotredesign.features.media.MediaContentRenderer
import im.vector.riotredesign.features.media.ImageContentRenderer
@EpoxyModelClass(layout = R.layout.item_timeline_event_image_message)
abstract class MessageImageItem : AbsMessageItem<MessageImageItem.Holder>() {
@EpoxyModelClass(layout = R.layout.item_timeline_event_image_video_message)
abstract class MessageImageVideoItem : AbsMessageItem<MessageImageVideoItem.Holder>() {
@EpoxyAttribute lateinit var mediaData: MediaContentRenderer.Data
@EpoxyAttribute lateinit var eventId: String
@EpoxyAttribute lateinit var mediaData: ImageContentRenderer.Data
@EpoxyAttribute override lateinit var informationData: MessageInformationData
@EpoxyAttribute var playable: Boolean = false
@EpoxyAttribute var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
MediaContentRenderer.render(mediaData, MediaContentRenderer.Mode.THUMBNAIL, holder.imageView)
ContentUploadStateTrackerBinder.bind(eventId, mediaData, holder.progressLayout)
ImageContentRenderer.render(mediaData, ImageContentRenderer.Mode.THUMBNAIL, holder.imageView)
ContentUploadStateTrackerBinder.bind(informationData.eventId, mediaData, holder.progressLayout)
holder.imageView.setOnClickListener(clickListener)
holder.imageView.isEnabled = !mediaData.isLocalFile()
holder.imageView.alpha = if (mediaData.isLocalFile()) 0.5f else 1f
holder.imageView.renderSendState()
holder.playContentView.visibility = if (playable) View.VISIBLE else View.GONE
}
override fun unbind(holder: Holder) {
ContentUploadStateTrackerBinder.unbind(eventId)
ContentUploadStateTrackerBinder.unbind(informationData.eventId)
super.unbind(holder)
}
@ -52,8 +52,9 @@ abstract class MessageImageItem : AbsMessageItem<MessageImageItem.Holder>() {
override val avatarImageView by bind<ImageView>(R.id.messageAvatarImageView)
override val memberNameView by bind<TextView>(R.id.messageMemberNameView)
override val timeView by bind<TextView>(R.id.messageTimeView)
val progressLayout by bind<ViewGroup>(R.id.messageImageUploadProgressLayout)
val imageView by bind<ImageView>(R.id.messageImageView)
val progressLayout by bind<ViewGroup>(R.id.messageMediaUploadProgressLayout)
val imageView by bind<ImageView>(R.id.messageThumbnailView)
val playContentView by bind<ImageView>(R.id.messageMediaPlayView)
}
}

View file

@ -16,7 +16,12 @@
package im.vector.riotredesign.features.home.room.detail.timeline.item
import im.vector.matrix.android.api.session.room.send.SendState
data class MessageInformationData(
val eventId: String,
val senderId: String,
val sendState: SendState,
val time: CharSequence? = null,
val avatarUrl: String?,
val memberName: CharSequence? = null,

View file

@ -45,6 +45,7 @@ abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
TextViewCompat.getTextMetricsParams(holder.messageView),
null)
holder.messageView.setTextFuture(textFuture)
holder.messageView.renderSendState()
findPillsAndProcess { it.bind(holder.messageView) }
}

View file

@ -30,11 +30,12 @@ abstract class NoticeItem : VectorEpoxyModel<NoticeItem.Holder>() {
@EpoxyAttribute var noticeText: CharSequence? = null
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var userId: String = ""
@EpoxyAttribute var memberName: CharSequence? = null
override fun bind(holder: Holder) {
holder.noticeTextView.text = noticeText
AvatarRenderer.render(avatarUrl, memberName?.toString(), holder.avatarImageView)
AvatarRenderer.render(avatarUrl, userId, memberName?.toString(), holder.avatarImageView)
}
class Holder : VectorEpoxyHolder() {

View file

@ -79,6 +79,7 @@ class RoomSummaryController(private val stringProvider: StringProvider
roomSummaryItem {
id(roomSummary.roomId)
roomId(roomSummary.roomId)
roomName(roomSummary.displayName)
avatarUrl(roomSummary.avatarUrl)
selected(isSelected)

View file

@ -31,6 +31,7 @@ import im.vector.riotredesign.features.home.AvatarRenderer
abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
@EpoxyAttribute lateinit var roomName: CharSequence
@EpoxyAttribute lateinit var roomId: String
@EpoxyAttribute var avatarUrl: String? = null
@EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute var unreadCount: Int = 0
@ -44,7 +45,7 @@ abstract class RoomSummaryItem : VectorEpoxyModel<RoomSummaryItem.Holder>() {
holder.rootView.isChecked = selected
holder.rootView.setOnClickListener { listener?.invoke() }
holder.titleView.text = roomName
AvatarRenderer.render(avatarUrl, roomName.toString(), holder.avatarImageView)
AvatarRenderer.render(avatarUrl, roomId, roomName.toString(), holder.avatarImageView)
}
class Holder : VectorEpoxyHolder() {

View file

@ -52,7 +52,7 @@ class PillImageSpan(private val glideRequests: GlideRequests,
@UiThread
fun bind(textView: TextView) {
tv = WeakReference(textView)
AvatarRenderer.render(context, glideRequests, user?.avatarUrl, displayName, target)
AvatarRenderer.render(context, glideRequests, user?.avatarUrl, userId, displayName, target)
}
// ReplacementSpan *****************************************************************************
@ -105,7 +105,7 @@ class PillImageSpan(private val glideRequests: GlideRequests,
textStartPadding = textPadding
setChipMinHeightResource(R.dimen.pill_min_height)
setChipIconSizeResource(R.dimen.pill_avatar_size)
chipIcon = AvatarRenderer.getPlaceholderDrawable(context, displayName)
chipIcon = AvatarRenderer.getPlaceholderDrawable(context, userId, displayName)
setBounds(0, 0, intrinsicWidth, intrinsicHeight)
}
}

View file

@ -27,7 +27,7 @@ import im.vector.riotredesign.core.glide.GlideApp
import kotlinx.android.parcel.Parcelize
import java.io.File
object MediaContentRenderer {
object ImageContentRenderer {
@Parcelize
data class Data(
@ -37,8 +37,8 @@ object MediaContentRenderer {
val maxHeight: Int,
val width: Int?,
val maxWidth: Int,
val orientation: Int?,
val rotation: Int?
val orientation: Int? = null,
val rotation: Int? = null
) : Parcelable {
fun isLocalFile(): Boolean {
@ -66,6 +66,7 @@ object MediaContentRenderer {
GlideApp
.with(imageView)
.load(resolvedUrl)
.dontAnimate()
.thumbnail(0.3f)
.into(imageView)
}
@ -73,16 +74,12 @@ object MediaContentRenderer {
fun render(data: Data, imageView: BigImageView) {
val (width, height) = processSize(data, Mode.THUMBNAIL)
val contentUrlResolver = Matrix.getInstance().currentSession!!.contentUrlResolver()
if (data.isLocalFile()) {
imageView.showImage(Uri.parse(data.url))
} else {
val fullSize = contentUrlResolver.resolveFullSize(data.url)
val thumbnail = contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
imageView.showImage(
Uri.parse(thumbnail),
Uri.parse(fullSize)
)
}
val fullSize = contentUrlResolver.resolveFullSize(data.url)
val thumbnail = contentUrlResolver.resolveThumbnail(data.url, width, height, ContentUrlResolver.ThumbnailMethod.SCALE)
imageView.showImage(
Uri.parse(thumbnail),
Uri.parse(fullSize)
)
}
private fun processSize(data: Data, mode: Mode): Pair<Int, Int> {

View file

@ -33,18 +33,18 @@ class MediaViewerActivity : VectorBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(im.vector.riotredesign.R.layout.activity_media_viewer)
val mediaData = intent.getParcelableExtra<MediaContentRenderer.Data>(EXTRA_MEDIA_DATA)
val mediaData = intent.getParcelableExtra<ImageContentRenderer.Data>(EXTRA_MEDIA_DATA)
if (mediaData.url.isNullOrEmpty()) {
finish()
} else {
configureToolbar(mediaViewerToolbar, mediaData)
mediaViewerImageView.setImageViewFactory(GlideImageViewFactory())
mediaViewerImageView.setProgressIndicator(ProgressPieIndicator())
MediaContentRenderer.render(mediaData, mediaViewerImageView)
ImageContentRenderer.render(mediaData, mediaViewerImageView)
}
}
private fun configureToolbar(toolbar: Toolbar, mediaData: MediaContentRenderer.Data) {
private fun configureToolbar(toolbar: Toolbar, mediaData: ImageContentRenderer.Data) {
setSupportActionBar(toolbar)
supportActionBar?.apply {
title = mediaData.filename
@ -57,7 +57,7 @@ class MediaViewerActivity : VectorBaseActivity() {
private const val EXTRA_MEDIA_DATA = "EXTRA_MEDIA_DATA"
fun newIntent(context: Context, mediaData: MediaContentRenderer.Data): Intent {
fun newIntent(context: Context, mediaData: ImageContentRenderer.Data): Intent {
return Intent(context, MediaViewerActivity::class.java).apply {
putExtra(EXTRA_MEDIA_DATA, mediaData)
}

View file

@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@ -43,6 +58,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:duplicateParentState="true"
android:textColor="@color/brown_grey"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
@ -50,7 +66,7 @@
tools:text="@tools:sample/date/hhmm" />
<ImageView
android:id="@+id/messageImageView"
android:id="@+id/messageThumbnailView"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="64dp"
@ -59,13 +75,28 @@
android:layout_marginEnd="32dp"
android:layout_marginRight="32dp"
android:layout_marginBottom="8dp"
android:duplicateParentState="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/messageMemberNameView" />
app:layout_constraintTop_toBottomOf="@+id/messageMemberNameView"
tools:layout_height="300dp" />
<ImageView
android:id="@+id/messageMediaPlayView"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_material_play_circle"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/messageThumbnailView"
app:layout_constraintEnd_toEndOf="@id/messageThumbnailView"
app:layout_constraintStart_toStartOf="@id/messageThumbnailView"
app:layout_constraintTop_toTopOf="@id/messageThumbnailView"
tools:visibility="visible" />
<include
android:id="@+id/messageImageUploadProgressLayout"
android:id="@+id/messageMediaUploadProgressLayout"
layout="@layout/media_upload_download_progress_layout"
android:layout_width="0dp"
android:layout_height="46dp"
@ -78,7 +109,7 @@
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/messageImageView"
app:layout_constraintTop_toBottomOf="@+id/messageThumbnailView"
tools:visibility="visible" />

View file

@ -44,6 +44,7 @@
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:textColor="@color/brown_grey"
android:duplicateParentState="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintTop_toTopOf="@id/messageMemberNameView"
@ -56,6 +57,7 @@
android:layout_marginStart="64dp"
android:layout_marginLeft="64dp"
android:layout_marginBottom="8dp"
android:duplicateParentState="true"
android:textColor="@color/dark_grey"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"