Skip to content
This repository has been archived by the owner on Jun 7, 2020. It is now read-only.

Fix Multi-Touch events on recyclerView items #2142

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ import kotlinx.android.synthetic.main.item_message_attachment.view.*
import timber.log.Timber

class AttachmentViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null,
var actionAttachmentOnClickListener: ActionAttachmentOnClickListener
) : BaseViewHolder<AttachmentUiModel>(itemView, listener, reactionListener) {
itemView: View,
listener: ActionsListener,
multiTouchEventsListener: MultiTouchEventsListener,
reactionListener: EmojiReactionListener? = null,
var actionAttachmentOnClickListener: ActionAttachmentOnClickListener
) : BaseViewHolder<AttachmentUiModel>(itemView, listener,multiTouchEventsListener ,reactionListener) {

private val messageViews = listOf<View>(
itemView.text_sender,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import chat.rocket.android.util.extensions.toList
import chat.rocket.core.model.Message
import chat.rocket.core.model.isSystemMessage
import com.google.android.flexbox.FlexDirection
import com.google.android.flexbox.FlexWrap
import com.google.android.flexbox.FlexboxLayoutManager
import com.google.android.flexbox.JustifyContent
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch

abstract class BaseViewHolder<T : BaseUiModel<*>>(
itemView: View,
private val listener: ActionsListener,
var reactionListener: EmojiReactionListener? = null
itemView: View,
private val listener: ActionsListener,
private val multiTouchEventsListener: MultiTouchEventsListener,
var reactionListener: EmojiReactionListener? = null
) : RecyclerView.ViewHolder(itemView),
MenuItem.OnMenuItemClickListener {
var data: T? = null
Expand Down Expand Up @@ -62,8 +64,9 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
}
}

override fun onReactionLongClicked(shortname: String, isCustom: Boolean, url: String?, usernames: List<String>) {
reactionListener?.onReactionLongClicked(shortname, isCustom,url, usernames)
override fun onReactionLongClicked(shortname: String, isCustom: Boolean,
url: String?, usernames: List<String>) {
reactionListener?.onReactionLongClicked(shortname, isCustom, url, usernames)
}
}

Expand All @@ -87,33 +90,45 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
fun onActionSelected(item: MenuItem, message: Message)
}

interface MultiTouchEventsListener {
fun handleMultiTouchEvents(status: Boolean)
}

private val onClickListener = { view: View ->
if (data?.message?.isSystemMessage() == false) {
data?.let { vm ->
vm.message.let {
val menuItems = view.context.inflate(R.menu.message_actions).toList()
menuItems.find { it.itemId == R.id.action_message_unpin }?.apply {
setTitle(if (it.pinned) R.string.action_msg_unpin else R.string.action_msg_pin)
isChecked = it.pinned
}
launch {
multiTouchEventsListener.handleMultiTouchEvents(false)
data?.let { vm ->
vm.message.let {
val menuItems = view.context.inflate(R.menu.message_actions).toList()
menuItems.find { it.itemId == R.id.action_message_unpin }?.apply {
setTitle(
if (it.pinned) R.string.action_msg_unpin else R.string.action_msg_pin)
isChecked = it.pinned
}

menuItems.find { it.itemId == R.id.action_message_star }?.apply {
val isStarred = it.starred?.isNotEmpty() ?: false
setTitle(if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star)
isChecked = isStarred
}
view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) {
if (this.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
menuItems.find { it.itemId == R.id.action_message_star }?.apply {
val isStarred = it.starred?.isNotEmpty() ?: false
setTitle(
if (isStarred) R.string.action_msg_unstar else R.string.action_msg_star)
isChecked = isStarred
}
view.context?.let {
if (it is ContextThemeWrapper && it.baseContext is AppCompatActivity) {
with(it.baseContext as AppCompatActivity) {
if (this.lifecycle.currentState.isAtLeast(
Lifecycle.State.RESUMED)) {
val actionsBottomSheet = MessageActionsBottomSheet()
actionsBottomSheet.addItems(menuItems, this@BaseViewHolder)
actionsBottomSheet.show(supportFragmentManager, null)
}
}
}
}
}
}
delay(500)
multiTouchEventsListener.handleMultiTouchEvents(true)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import chat.rocket.android.chatroom.uimodel.MessageUiModel
import chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import chat.rocket.android.chatroom.uimodel.toViewType
import chat.rocket.android.emoji.EmojiReactionListener
import chat.rocket.android.util.RCRecyclerView
import chat.rocket.android.util.extensions.inflate
import chat.rocket.android.util.extensions.openTabbedUrl
import chat.rocket.core.model.Message
Expand All @@ -23,15 +24,16 @@ import timber.log.Timber
import java.security.InvalidParameterException

class ChatRoomAdapter(
private val roomId: String? = null,
private val roomType: String? = null,
private val roomName: String? = null,
private val actionSelectListener: OnActionSelected? = null,
private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null,
private val navigator: ChatRoomNavigator? = null
private val roomId: String? = null,
private val roomType: String? = null,
private val roomName: String? = null,
private val actionSelectListener: OnActionSelected? = null,
private val enableActions: Boolean = true,
private val reactionListener: EmojiReactionListener? = null,
private val navigator: ChatRoomNavigator? = null
) : RecyclerView.Adapter<BaseViewHolder<*>>() {
private val dataSet = ArrayList<BaseUiModel<*>>()
private lateinit var ownRecyclerView: RCRecyclerView

init {
setHasStableIds(true)
Expand All @@ -42,30 +44,38 @@ class ChatRoomAdapter(
BaseUiModel.ViewType.MESSAGE -> {
val view = parent.inflate(R.layout.item_message)
MessageViewHolder(
view,
actionsListener,
reactionListener
view,
actionsListener,
multiTouchEventListener,
reactionListener
) { userId -> navigator?.toUserDetails(userId) }
}
BaseUiModel.ViewType.URL_PREVIEW -> {
val view = parent.inflate(R.layout.message_url_preview)
UrlPreviewViewHolder(view, actionsListener, reactionListener)
UrlPreviewViewHolder(
view,
actionsListener,
multiTouchEventListener,
reactionListener
)
}
BaseUiModel.ViewType.ATTACHMENT -> {
val view = parent.inflate(R.layout.item_message_attachment)
AttachmentViewHolder(
view,
actionsListener,
reactionListener,
actionAttachmentOnClickListener
view,
actionsListener,
multiTouchEventListener,
reactionListener,
actionAttachmentOnClickListener
)
}
BaseUiModel.ViewType.MESSAGE_REPLY -> {
val view = parent.inflate(R.layout.item_message_reply)
MessageReplyViewHolder(
view,
actionsListener,
reactionListener
view,
actionsListener,
multiTouchEventListener,
reactionListener
) { roomName, permalink ->
actionSelectListener?.openDirectMessage(roomName, permalink)
}
Expand Down Expand Up @@ -119,6 +129,11 @@ class ChatRoomAdapter(
}
}

override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
ownRecyclerView = recyclerView as RCRecyclerView
}

fun clearData() {
dataSet.clear()
notifyDataSetChanged()
Expand All @@ -134,7 +149,7 @@ class ChatRoomAdapter(
//---At first we will update all already saved elements with received updated ones
val filteredDataSet = dataSet.filter { newItem ->
val matchedIndex =
this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType }
this.dataSet.indexOfFirst { it.messageId == newItem.messageId && it.viewType == newItem.viewType }
if (matchedIndex > -1) {
this.dataSet[matchedIndex] = newItem
notifyItemChanged(matchedIndex)
Expand Down Expand Up @@ -227,6 +242,12 @@ class ChatRoomAdapter(
}
}

private val multiTouchEventListener = object : BaseViewHolder.MultiTouchEventsListener {
override fun handleMultiTouchEvents(status: Boolean) {
ownRecyclerView.setDispatchTouchStatus(status)
}
}

private val actionsListener = object : BaseViewHolder.ActionsListener {

override fun isActionsEnabled(): Boolean = enableActions
Expand Down Expand Up @@ -284,10 +305,10 @@ class ChatRoomAdapter(
fun showMessageInfo(id: String)

fun citeMessage(
roomName: String,
roomType: String,
messageId: String,
mentionAuthor: Boolean
roomName: String,
roomType: String,
messageId: String,
mentionAuthor: Boolean
)

fun copyMessage(id: String)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import chat.rocket.android.emoji.EmojiReactionListener
import kotlinx.android.synthetic.main.item_message_reply.view.*

class MessageReplyViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null,
private val replyCallback: (roomName: String, permalink: String) -> Unit
) : BaseViewHolder<MessageReplyUiModel>(itemView, listener, reactionListener) {
itemView: View,
listener: ActionsListener,
multiTouchEventsListener: MultiTouchEventsListener,
reactionListener: EmojiReactionListener? = null,
private val replyCallback: (roomName: String, permalink: String) -> Unit
) : BaseViewHolder<MessageReplyUiModel>(itemView, listener,multiTouchEventsListener ,reactionListener) {

init {
setupActionMenu(itemView)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.*

class MessageViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null,
private val avatarListener: (String) -> Unit
) : BaseViewHolder<MessageUiModel>(itemView, listener, reactionListener), Drawable.Callback {
itemView: View,
listener: ActionsListener,
multiTouchEventsListener: MultiTouchEventsListener,
reactionListener: EmojiReactionListener? = null,
private val avatarListener: (String) -> Unit
) : BaseViewHolder<MessageUiModel>(itemView, listener,multiTouchEventsListener, reactionListener), Drawable.Callback {

init {
with(itemView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import chat.rocket.android.util.extensions.openTabbedUrl
import kotlinx.android.synthetic.main.message_url_preview.view.*

class UrlPreviewViewHolder(
itemView: View,
listener: ActionsListener,
reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<UrlPreviewUiModel>(itemView, listener, reactionListener) {
itemView: View,
listener: ActionsListener,
multiTouchEventsListener: MultiTouchEventsListener,
reactionListener: EmojiReactionListener? = null
) : BaseViewHolder<UrlPreviewUiModel>(itemView, listener,multiTouchEventsListener, reactionListener) {

init {
setupActionMenu(itemView.url_preview_layout)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,26 @@ import chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import chat.rocket.android.util.extension.setTextViewAppearance
import chat.rocket.common.model.RoomType
import chat.rocket.common.model.UserStatus
import kotlinx.android.synthetic.main.item_chat.view.*
import kotlinx.android.synthetic.main.unread_messages_badge.view.*
import kotlinx.android.synthetic.main.item_chat.view.image_avatar
import kotlinx.android.synthetic.main.item_chat.view.image_chat_icon
import kotlinx.android.synthetic.main.item_chat.view.text_chat_name
import kotlinx.android.synthetic.main.item_chat.view.text_last_message
import kotlinx.android.synthetic.main.item_chat.view.text_timestamp
import kotlinx.android.synthetic.main.unread_messages_badge.view.text_total_unread_messages
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch

class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit) :
ViewHolder<RoomItemHolder>(itemView) {
class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit,
private val multiTouchEventsListener: MultiTouchEventsListener) :
ViewHolder<RoomItemHolder>(itemView) {
private val resources: Resources = itemView.resources
private val channelIcon: Drawable = resources.getDrawable(R.drawable.ic_hashtag_12dp, null)
private val groupIcon: Drawable = resources.getDrawable(R.drawable.ic_lock_12_dp, null)
private val onlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_online_12dp, null)
private val awayIcon: Drawable = resources.getDrawable(R.drawable.ic_status_away_12dp, null)
private val busyIcon: Drawable = resources.getDrawable(R.drawable.ic_status_busy_12dp, null)
private val offlineIcon: Drawable = resources.getDrawable(R.drawable.ic_status_invisible_12dp, null)
private val offlineIcon: Drawable =
resources.getDrawable(R.drawable.ic_status_invisible_12dp, null)

override fun bindViews(data: RoomItemHolder) {
val room = data.data
Expand Down Expand Up @@ -54,21 +62,37 @@ class RoomViewHolder(itemView: View, private val listener: (RoomUiModel) -> Unit
if (room.unread == null) text_total_unread_messages.text = "!"
if (room.unread != null) text_total_unread_messages.text = room.unread
if (room.mentions) text_total_unread_messages.text = "@${room.unread}"
text_chat_name.setTextViewAppearance(context, R.style.ChatList_ChatName_Unread_TextView)
text_timestamp.setTextViewAppearance(context, R.style.ChatList_Timestamp_Unread_TextView)
text_last_message.setTextViewAppearance(context, R.style.ChatList_LastMessage_Unread_TextView)
text_chat_name.setTextViewAppearance(context,
R.style.ChatList_ChatName_Unread_TextView)
text_timestamp.setTextViewAppearance(context,
R.style.ChatList_Timestamp_Unread_TextView)
text_last_message.setTextViewAppearance(context,
R.style.ChatList_LastMessage_Unread_TextView)
text_total_unread_messages.isVisible = true
} else {
text_chat_name.setTextViewAppearance(context, R.style.ChatList_ChatName_TextView)
text_timestamp.setTextViewAppearance(context, R.style.ChatList_Timestamp_TextView)
text_last_message.setTextViewAppearance(context, R.style.ChatList_LastMessage_TextView)
text_last_message.setTextViewAppearance(context,
R.style.ChatList_LastMessage_TextView)
text_total_unread_messages.isInvisible = true
}

setOnClickListener { listener(room) }
setOnClickListener {
launch {
multiTouchEventsListener.handleMultiTouchEvents(false)
listener(room)
delay(500)
multiTouchEventsListener.handleMultiTouchEvents(true)
}
}
}
}


interface MultiTouchEventsListener {
fun handleMultiTouchEvents(status: Boolean)
}

private fun getRoomDrawable(type: RoomType): Drawable? = when (type) {
is RoomType.Channel -> channelIcon
is RoomType.PrivateGroup -> groupIcon
Expand Down
Loading