diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/ActionsAttachmentViewHolder.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/ActionsAttachmentViewHolder.kt new file mode 100644 index 0000000000..11fa0ae1db --- /dev/null +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/ActionsAttachmentViewHolder.kt @@ -0,0 +1,43 @@ +package chat.rocket.android.chatroom.adapter + +import android.view.View +import chat.rocket.android.chatroom.uimodel.ActionsAttachmentUiModel +import chat.rocket.android.emoji.EmojiReactionListener +import chat.rocket.core.model.attachment.actions.Action +import chat.rocket.core.model.attachment.actions.ButtonAction +import kotlinx.android.synthetic.main.item_actions_attachment.view.* +import androidx.recyclerview.widget.LinearLayoutManager +import timber.log.Timber + +class ActionsAttachmentViewHolder( + itemView: View, + listener: ActionsListener, + reactionListener: EmojiReactionListener? = null, + var actionAttachmentOnClickListener: ActionAttachmentOnClickListener +) : BaseViewHolder(itemView, listener, reactionListener) { + + init { + with(itemView) { + setupActionMenu(actions_attachment_container) + } + } + + override fun bindViews(data: ActionsAttachmentUiModel) { + val actions = data.actions + val alignment = data.buttonAlignment + Timber.d("no of actions : ${actions.size} : $actions") + with(itemView) { + title.text = data.title ?: "" + actions_list.layoutManager = LinearLayoutManager(itemView.context, + when (alignment) { + "horizontal" -> LinearLayoutManager.HORIZONTAL + else -> LinearLayoutManager.VERTICAL //Default + }, false) + actions_list.adapter = ActionsListAdapter(actions, actionAttachmentOnClickListener) + } + } +} + +interface ActionAttachmentOnClickListener { + fun onActionClicked(view: View, action: Action) +} \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/ActionsListAdapter.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/ActionsListAdapter.kt new file mode 100644 index 0000000000..35a1f84269 --- /dev/null +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/ActionsListAdapter.kt @@ -0,0 +1,73 @@ +package chat.rocket.android.chatroom.adapter + +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import chat.rocket.android.R +import chat.rocket.android.util.extensions.inflate +import chat.rocket.core.model.attachment.actions.Action +import chat.rocket.core.model.attachment.actions.ButtonAction +import com.facebook.drawee.backends.pipeline.Fresco +import kotlinx.android.synthetic.main.item_action_button.view.* +import timber.log.Timber + +class ActionsListAdapter(actions: List, var actionAttachmentOnClickListener: ActionAttachmentOnClickListener) : RecyclerView.Adapter() { + + var actions: List = actions + + inner class ViewHolder(var layout: View) : RecyclerView.ViewHolder(layout) { + lateinit var action: ButtonAction + + private val onClickListener = View.OnClickListener { + actionAttachmentOnClickListener.onActionClicked(it, action) + } + + init { + with(itemView) { + action_button.setOnClickListener(onClickListener) + action_image_button.setOnClickListener(onClickListener) + } + } + + fun bindAction(action: Action) { + with(itemView) { + Timber.d("action : $action") + this@ViewHolder.action = action as ButtonAction + + if (action.imageUrl != null) { + action_button.isVisible = false + action_image_button.isVisible = true + + //Image button + val controller = Fresco.newDraweeControllerBuilder().apply { + setUri(action.imageUrl) + autoPlayAnimations = true + oldController = action_image_button.controller + }.build() + action_image_button.controller = controller + + } else if (action.text != null) { + action_button.isVisible = true + action_image_button.isVisible = false + + this.action_button.setText(action.text) + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = parent.inflate(R.layout.item_action_button) + return ViewHolder(view) + } + + override fun getItemCount(): Int { + return actions.size + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val action = actions[position] + holder.bindAction(action) + } +} \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt b/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt index df4ae519e5..cdc3874384 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt @@ -1,21 +1,23 @@ package chat.rocket.android.chatroom.adapter -import android.app.AlertDialog -import android.content.Context import androidx.recyclerview.widget.RecyclerView import android.view.MenuItem +import android.view.View import android.view.ViewGroup import chat.rocket.android.R -import chat.rocket.android.chatroom.presentation.ChatRoomPresenter import chat.rocket.android.chatroom.uimodel.* import chat.rocket.android.util.extensions.inflate import chat.rocket.android.emoji.EmojiReactionListener +import chat.rocket.android.util.extensions.openTabbedUrl +import chat.rocket.core.model.attachment.actions.Action +import chat.rocket.core.model.attachment.actions.ButtonAction import chat.rocket.core.model.Message import chat.rocket.core.model.isSystemMessage 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, @@ -72,6 +74,10 @@ class ChatRoomAdapter( actionSelectListener?.openDirectMessage(roomName, permalink) } } + BaseUiModel.ViewType.ACTIONS_ATTACHMENT -> { + val view = parent.inflate(R.layout.item_actions_attachment) + ActionsAttachmentViewHolder(view, actionsListener, reactionListener, actionAttachmentOnClickListener) + } else -> { throw InvalidParameterException("TODO - implement for ${viewType.toViewType()}") } @@ -125,6 +131,8 @@ class ChatRoomAdapter( holder.bind(dataSet[position] as GenericFileAttachmentUiModel) is MessageReplyViewHolder -> holder.bind(dataSet[position] as MessageReplyUiModel) + is ActionsAttachmentViewHolder -> + holder.bind(dataSet[position] as ActionsAttachmentUiModel) } } @@ -203,6 +211,33 @@ class ChatRoomAdapter( } } + private val actionAttachmentOnClickListener = object : ActionAttachmentOnClickListener { + override fun onActionClicked(view: View, action: Action) { + val temp = action as ButtonAction + if (temp.url != null && temp.isWebView != null) { + if (temp.isWebView == true) { + //TODO: Open in a configurable sizable webview + Timber.d("Open in a configurable sizable webview") + } else { + //Open in chrome custom tab + temp.url?.let { view.openTabbedUrl(it) } + } + } else if (temp.message != null && temp.isMessageInChatWindow != null) { + if (temp.isMessageInChatWindow == true) { + //Send to chat window + temp.message?.let { + if (roomId != null) { + actionSelectListener?.sendMessage(roomId, it) + } + } + } else { + //TODO: Send to bot but not in chat window + Timber.d("Send to bot but not in chat window") + } + } + } + } + private val actionsListener = object : BaseViewHolder.ActionsListener { override fun isActionsEnabled(): Boolean = enableActions @@ -259,5 +294,6 @@ class ChatRoomAdapter( fun deleteMessage(roomId: String, id: String) fun showReactions(id: String) fun openDirectMessage(roomName: String, message: String) + fun sendMessage(chatRoomId: String, text: String) } } \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt index 238723b0e5..37c91cc756 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt @@ -209,7 +209,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR requireNotNull(bundle) { "no arguments supplied when the fragment was instantiated" } } - adapter = ChatRoomAdapter(chatRoomType, chatRoomName, this, + adapter = ChatRoomAdapter(chatRoomId, chatRoomType, chatRoomName, this, reactionListener = this) } @@ -1026,4 +1026,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR override fun openDirectMessage(roomName: String, message: String) { presenter.openDirectMessage(roomName, message) } + + override fun sendMessage(chatRoomId: String, text: String) { + presenter.sendMessage(chatRoomId, text, null) + } } diff --git a/app/src/main/java/chat/rocket/android/chatroom/uimodel/ActionsAttachmentUiModel.kt b/app/src/main/java/chat/rocket/android/chatroom/uimodel/ActionsAttachmentUiModel.kt new file mode 100644 index 0000000000..165936630b --- /dev/null +++ b/app/src/main/java/chat/rocket/android/chatroom/uimodel/ActionsAttachmentUiModel.kt @@ -0,0 +1,29 @@ +package chat.rocket.android.chatroom.uimodel + +import chat.rocket.android.R +import chat.rocket.core.model.Message +import chat.rocket.core.model.attachment.actions.Action +import chat.rocket.core.model.attachment.actions.ActionsAttachment + +data class ActionsAttachmentUiModel( + override val attachmentUrl: String, + val title: String?, + val actions: List, + val buttonAlignment: String, + override val message: Message, + override val rawData: ActionsAttachment, + override val messageId: String, + override var reactions: List, + override var nextDownStreamMessage: BaseUiModel<*>? = null, + override var preview: Message? = null, + override var isTemporary: Boolean = false, + override var unread: Boolean? = null, + override var menuItemsToHide: MutableList = mutableListOf(), + override var currentDayMarkerText: String, + override var showDayMarker: Boolean +) : BaseAttachmentUiModel { + override val viewType: Int + get() = BaseUiModel.ViewType.ACTIONS_ATTACHMENT.viewType + override val layoutId: Int + get() = R.layout.item_actions_attachment +} \ No newline at end of file diff --git a/app/src/main/java/chat/rocket/android/chatroom/uimodel/BaseUiModel.kt b/app/src/main/java/chat/rocket/android/chatroom/uimodel/BaseUiModel.kt index d181e9dc78..a67602bbd7 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/uimodel/BaseUiModel.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/uimodel/BaseUiModel.kt @@ -29,7 +29,8 @@ interface BaseUiModel { AUTHOR_ATTACHMENT(7), COLOR_ATTACHMENT(8), GENERIC_FILE_ATTACHMENT(9), - MESSAGE_REPLY(10) + MESSAGE_REPLY(10), + ACTIONS_ATTACHMENT(11) } } diff --git a/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt b/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt index f62f5101bd..6d0c074335 100644 --- a/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt +++ b/app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt @@ -46,6 +46,7 @@ import chat.rocket.core.model.attachment.GenericFileAttachment import chat.rocket.core.model.attachment.ImageAttachment import chat.rocket.core.model.attachment.MessageAttachment import chat.rocket.core.model.attachment.VideoAttachment +import chat.rocket.core.model.attachment.actions.ActionsAttachment import chat.rocket.core.model.isSystemMessage import chat.rocket.core.model.url.Url import kotlinx.coroutines.experimental.CommonPool @@ -305,10 +306,26 @@ class UiModelMapper @Inject constructor( is MessageAttachment -> mapMessageAttachment(message, attachment) is AuthorAttachment -> mapAuthorAttachment(message, attachment) is ColorAttachment -> mapColorAttachment(message, attachment) + is ActionsAttachment -> mapActionsAttachment(message, attachment) else -> null } } + private fun mapActionsAttachment(message: Message, attachment: ActionsAttachment): BaseUiModel<*>? { + return with(attachment) { + val content = stripMessageQuotes(message) + + val localDateTime = DateTimeHelper.getLocalDateTime(message.timestamp) + val dayMarkerText = DateTimeHelper.getFormattedDateForMessages(localDateTime, context) + + ActionsAttachmentUiModel(attachmentUrl = url, title = title, + actions = actions, buttonAlignment = buttonAlignment, message = message, rawData = attachment, + messageId = message.id, reactions = getReactions(message), + preview = message.copy(message = content.message), unread = message.unread, + showDayMarker = false, currentDayMarkerText = dayMarkerText) + } + } + private fun mapColorAttachment(message: Message, attachment: ColorAttachment): BaseUiModel<*>? { return with(attachment) { val content = stripMessageQuotes(message) diff --git a/app/src/main/res/layout/item_action_button.xml b/app/src/main/res/layout/item_action_button.xml new file mode 100644 index 0000000000..62c470b24e --- /dev/null +++ b/app/src/main/res/layout/item_action_button.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_actions_attachment.xml b/app/src/main/res/layout/item_actions_attachment.xml new file mode 100644 index 0000000000..247a15dca9 --- /dev/null +++ b/app/src/main/res/layout/item_actions_attachment.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + \ No newline at end of file