Debounce click + avatar click
This commit is contained in:
parent
38abf31889
commit
8fd15f4082
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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?) {
|
||||
|
|
|
@ -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>()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue