Debounce click + avatar click

This commit is contained in:
Valere 2019-05-08 15:49:32 +02:00
parent 38abf31889
commit 8fd15f4082
6 changed files with 135 additions and 17 deletions

View File

@ -0,0 +1,23 @@
package im.vector.riotredesign.core.utils
import android.view.View
/**
* Simple Debounced OnClickListener
* Safe to use in different views
*/
class DebouncedClickListener(val original: View.OnClickListener, private val minimumInterval: Long = 400) : View.OnClickListener {
private val lastClickMap = HashMap<View, Long>()
override fun onClick(clickedView: View) {
val previousClickTimestamp = lastClickMap[clickedView]
val currentTimestamp = System.currentTimeMillis()
lastClickMap[clickedView] = currentTimestamp
if (previousClickTimestamp == null || currentTimestamp - previousClickTimestamp.toLong() > minimumInterval) {
original.onClick(clickedView)
}
}
}

View File

@ -49,11 +49,7 @@ 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.MessageAudioContent
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
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.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.api.session.user.model.User
import im.vector.reactions.EmojiReactionPickerActivity
@ -429,6 +425,17 @@ class RoomDetailFragment :
vectorBaseActivity.notImplemented()
}
override fun onEventCellClicked(eventId: String, informationData: MessageInformationData, messageContent: MessageContent, view: View) {
val roomId = (arguments?.get(MvRx.KEY_ARG) as? RoomDetailArgs)?.roomId
if (roomId.isNullOrBlank()) {
Timber.e("Missing RoomId, cannot open bottomsheet")
return
}
MessageActionsBottomSheet
.newInstance(eventId, roomId, informationData)
.show(requireActivity().supportFragmentManager, "MESSAGE_CONTEXTUAL_ACTIONS")
}
// AutocompleteUserPresenter.Callback
override fun onEventLongClicked(eventId: String, informationData: MessageInformationData, messageContent: MessageContent, view: View): Boolean {
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
@ -443,7 +450,9 @@ class RoomDetailFragment :
return true
}
override fun onAvatarClicked(informationData: MessageInformationData) {
vectorBaseActivity.notImplemented()
}
// AutocompleteUserPresenter.Callback
override fun onQueryUsers(query: CharSequence?) {

View File

@ -54,7 +54,9 @@ class TimelineEventController(private val dateFormatter: TimelineDateFormatter,
fun onVideoMessageClicked(messageVideoContent: MessageVideoContent, mediaData: VideoContentRenderer.Data, view: View)
fun onFileMessageClicked(messageFileContent: MessageFileContent)
fun onAudioMessageClicked(messageAudioContent: MessageAudioContent)
fun onEventCellClicked(eventId: String, informationData: MessageInformationData, messageContent: MessageContent, view: View)
fun onEventLongClicked(eventId: String, informationData: MessageInformationData, messageContent: MessageContent, view: View): Boolean
fun onAvatarClicked(informationData: MessageInformationData)
}
private val collapsedEventIds = linkedSetOf<String>()

View File

@ -16,11 +16,9 @@
package im.vector.riotredesign.features.home.room.detail.timeline.factory
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.view.View
import androidx.annotation.ColorRes
import androidx.core.text.toSpannable
import im.vector.matrix.android.api.permalinks.MatrixLinkify
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
import im.vector.matrix.android.api.session.events.model.EventType
@ -33,6 +31,7 @@ import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.linkify.VectorLinkify
import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.core.utils.DebouncedClickListener
import im.vector.riotredesign.features.home.room.detail.timeline.TimelineEventController
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
@ -102,7 +101,18 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.informationData(informationData)
.filename(messageContent.body)
.iconRes(R.drawable.filetype_audio)
.clickListener { _ -> callback?.onAudioMessageClicked(messageContent) }
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.clickListener(
DebouncedClickListener(View.OnClickListener { _ ->
callback?.onAudioMessageClicked(messageContent)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
?: false
@ -116,7 +126,18 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.informationData(informationData)
.filename(messageContent.body)
.iconRes(R.drawable.filetype_attachment)
.clickListener { _ -> callback?.onFileMessageClicked(messageContent) }
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.clickListener(
DebouncedClickListener(View.OnClickListener { _ ->
callback?.onFileMessageClicked(messageContent)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
?: false
@ -147,7 +168,19 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.playable(messageContent.info?.mimeType == "image/gif")
.informationData(informationData)
.mediaData(data)
.clickListener { view -> callback?.onImageMessageClicked(messageContent, data, view) }
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
.clickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onImageMessageClicked(messageContent, data, view)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
?: false
@ -178,6 +211,14 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
.playable(true)
.informationData(informationData)
.mediaData(thumbnailData)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.clickListener { view -> callback?.onVideoMessageClicked(messageContent, videoData, view) }
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
@ -198,6 +239,19 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return MessageTextItem_()
.message(linkifiedBody)
.informationData(informationData)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
//click on the text
.clickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
?: false
@ -219,6 +273,14 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return MessageTextItem_()
.message(message)
.informationData(informationData)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
?: false
@ -236,6 +298,14 @@ class MessageItemFactory(private val colorProvider: ColorProvider,
return MessageTextItem_()
.message(message)
.informationData(informationData)
.avatarClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onAvatarClicked(informationData)
}))
.cellClickListener(
DebouncedClickListener(View.OnClickListener { view ->
callback?.onEventCellClicked(eventId, informationData, messageContent, view)
}))
.longClickListener { view ->
return@longClickListener callback?.onEventLongClicked(eventId, informationData, messageContent, view)
?: false

View File

@ -20,6 +20,7 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.jakewharton.rxbinding2.view.RxView
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.features.home.AvatarRenderer
@ -31,10 +32,17 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>()
@EpoxyAttribute
var longClickListener: View.OnLongClickListener? = null
@EpoxyAttribute
var cellClickListener: View.OnClickListener? = null
@EpoxyAttribute
var avatarClickListener: View.OnClickListener? = null
override fun bind(holder: H) {
super.bind(holder)
if (informationData.showInformation) {
holder.avatarImageView.visibility = View.VISIBLE
holder.avatarImageView.setOnClickListener(avatarClickListener)
holder.memberNameView.visibility = View.VISIBLE
holder.timeView.visibility = View.VISIBLE
holder.timeView.text = informationData.time
@ -45,6 +53,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : VectorEpoxyModel<H>()
holder.memberNameView.visibility = View.GONE
holder.timeView.visibility = View.GONE
}
holder.view.setOnClickListener(cellClickListener)
holder.view.setOnLongClickListener(longClickListener)
}

View File

@ -16,7 +16,7 @@
package im.vector.riotredesign.features.home.room.detail.timeline.item
import android.text.Spannable
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.widget.AppCompatTextView
@ -36,17 +36,22 @@ import kotlinx.coroutines.withContext
@EpoxyModelClass(layout = R.layout.item_timeline_event_text_message)
abstract class MessageTextItem : AbsMessageItem<MessageTextItem.Holder>() {
@EpoxyAttribute var message: CharSequence? = null
@EpoxyAttribute override lateinit var informationData: MessageInformationData
@EpoxyAttribute
var message: CharSequence? = null
@EpoxyAttribute
override lateinit var informationData: MessageInformationData
@EpoxyAttribute
var clickListener: View.OnClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
MatrixLinkify.addLinkMovementMethod(holder.messageView)
val textFuture = PrecomputedTextCompat.getTextFuture(message ?: "",
TextViewCompat.getTextMetricsParams(holder.messageView),
null)
TextViewCompat.getTextMetricsParams(holder.messageView),
null)
holder.messageView.setTextFuture(textFuture)
holder.messageView.renderSendState()
holder.messageView.setOnClickListener (clickListener)
holder.messageView.setOnLongClickListener(longClickListener)
findPillsAndProcess { it.bind(holder.messageView) }
}