mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Adjust colors for avatar and display names + start handling video in timeline
This commit is contained in:
parent
c38a601bcc
commit
9c9c09db2b
21 changed files with 188 additions and 100 deletions
|
@ -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 { _ ->
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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?) {
|
||||
|
|
|
@ -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<*>>>()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue