-
Notifications
You must be signed in to change notification settings - Fork 553
[NEW] Add support for initial Rich Messaging #1557
Changes from 4 commits
8266b7d
4000400
611b008
00aa5de
ee657f5
894b61e
433c545
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
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<ActionsAttachmentUiModel>(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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can resume the condition in this way:
or, if you prefer to use
Not needed to repeat |
||
"vertical" -> LinearLayoutManager.VERTICAL | ||
"horizontal" -> LinearLayoutManager.HORIZONTAL | ||
else -> LinearLayoutManager.VERTICAL //Default | ||
}, false) | ||
actions_list.adapter = ActionsListAdapter(actions, actionAttachmentOnClickListener) | ||
} | ||
} | ||
} | ||
|
||
interface ActionAttachmentOnClickListener { | ||
fun onActionClicked(view: View, action: Action) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Action>, var actionAttachmentOnClickListener: ActionAttachmentOnClickListener) : RecyclerView.Adapter<ActionsListAdapter.ViewHolder>() { | ||
|
||
var actions: List<Action> = 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a good usage of 👍 |
||
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) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,20 +2,26 @@ package chat.rocket.android.chatroom.adapter | |
|
||
import android.app.AlertDialog | ||
import android.content.Context | ||
import android.net.Uri | ||
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 +78,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 +135,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 +215,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!!) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you use |
||
//TODO: Open in a configurable sizable webview | ||
Timber.d("Open in a configurable sizable webview") | ||
} else { | ||
//Open in chrome custom tab | ||
view.openTabbedUrl(Uri.parse(temp.url)) | ||
} | ||
} else if (temp.message != null && temp.isMessageInChatWindow != null) { | ||
if (temp.isMessageInChatWindow!!) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here |
||
//Send to chat window | ||
temp.message.run { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use |
||
if (roomId != null) { | ||
presenter?.sendMessage(roomId, temp.message!!, null) | ||
} | ||
} | ||
} 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 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -339,6 +339,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR | |
} | ||
|
||
if (recycler_view.adapter == null) { | ||
adapter = ChatRoomAdapter( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The adapter is already initialized on |
||
chatRoomId, | ||
chatRoomType, | ||
chatRoomName, | ||
presenter, | ||
reactionListener = this@ChatRoomFragment, | ||
context = context | ||
) | ||
recycler_view.adapter = adapter | ||
if (dataSet.size >= 30) { | ||
recycler_view.addOnScrollListener(endlessRecyclerViewScrollListener) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use 4 spaces indentation here, please. |
||
val title: String?, | ||
val actions: List<Action>, | ||
val buttonAlignment: String, | ||
override val message: Message, | ||
override val rawData: ActionsAttachment, | ||
override val messageId: String, | ||
override var reactions: List<ReactionUiModel>, | ||
override var nextDownStreamMessage: BaseUiModel<*>? = null, | ||
override var preview: Message? = null, | ||
override var isTemporary: Boolean = false, | ||
override var unread: Boolean? = null, | ||
override var menuItemsToHide: MutableList<Int> = mutableListOf(), | ||
override var currentDayMarkerText: String, | ||
override var showDayMarker: Boolean | ||
) : BaseAttachmentUiModel<ActionsAttachment> { | ||
override val viewType: Int | ||
get() = BaseUiModel.ViewType.ACTIONS_ATTACHMENT.viewType | ||
override val layoutId: Int | ||
get() = R.layout.item_actions_attachment | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:fresco="http://schemas.android.com/apk/res-auto" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:orientation="horizontal"> | ||
|
||
<com.google.android.material.button.MaterialButton | ||
android:id="@+id/action_button" | ||
style="@style/Widget.MaterialComponents.Button" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:layout_marginEnd="2dp" | ||
android:layout_marginStart="2dp" | ||
android:textAppearance="@style/TextAppearance.AppCompat.Body2" | ||
android:textSize="12sp" /> | ||
|
||
<com.facebook.drawee.view.SimpleDraweeView | ||
android:id="@+id/action_image_button" | ||
android:layout_width="match_parent" | ||
android:layout_height="75dp" | ||
android:layout_marginBottom="10dp" | ||
android:layout_marginEnd="2dp" | ||
android:layout_marginStart="2dp" | ||
android:visibility="gone" | ||
fresco:actualImageScaleType="fitStart" | ||
fresco:placeholderImage="@drawable/image_dummy" /> | ||
|
||
</RelativeLayout> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<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" | ||
android:id="@+id/actions_attachment_container" | ||
android:layout_width="match_parent" | ||
android:layout_height="wrap_content" | ||
android:background="?android:attr/selectableItemBackground" | ||
android:clickable="true" | ||
android:focusable="true" | ||
android:paddingBottom="@dimen/message_item_top_and_bottom_padding" | ||
android:paddingEnd="@dimen/screen_edge_left_and_right_padding" | ||
android:paddingStart="@dimen/screen_edge_left_and_right_padding"> | ||
|
||
<TextView | ||
android:id="@+id/title" | ||
style="@style/Message.TextView" | ||
android:layout_width="0dp" | ||
android:layout_height="wrap_content" | ||
android:layout_marginBottom="4dp" | ||
android:layout_marginStart="56dp" | ||
android:layout_marginTop="2dp" | ||
android:textDirection="locale" | ||
app:layout_constraintEnd_toEndOf="parent" | ||
app:layout_constraintStart_toStartOf="parent" | ||
tools:text="This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!" /> | ||
|
||
<View | ||
android:id="@+id/quote_bar" | ||
android:layout_width="4dp" | ||
android:layout_height="0dp" | ||
android:background="@drawable/quote_vertical_gray_bar" | ||
app:layout_constraintBottom_toTopOf="@id/recycler_view_reactions" | ||
app:layout_constraintStart_toStartOf="@id/title" | ||
app:layout_constraintTop_toBottomOf="@id/title" /> | ||
|
||
|
||
<androidx.recyclerview.widget.RecyclerView | ||
android:id="@+id/actions_list" | ||
android:layout_width="0dp" | ||
android:layout_height="wrap_content" | ||
android:layout_marginStart="8dp" | ||
android:textAppearance="@style/TextAppearance.AppCompat.Body2" | ||
android:textColor="@color/colorAccent" | ||
android:textDirection="locale" | ||
app:layout_constraintEnd_toEndOf="@id/title" | ||
app:layout_constraintStart_toEndOf="@id/quote_bar" | ||
app:layout_constraintTop_toBottomOf="@id/title" /> | ||
|
||
<include | ||
layout="@layout/layout_reactions" | ||
android:layout_width="0dp" | ||
android:layout_height="wrap_content" | ||
app:layout_constraintStart_toStartOf="@id/quote_bar" | ||
app:layout_constraintTop_toBottomOf="@id/actions_list" /> | ||
|
||
</androidx.constraintlayout.widget.ConstraintLayout> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with
is useless here since there is only one block being used. Could beitemView.setupActionMenu(actions_attachment_container)
instead.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any issue using
with
even if it is just for one statement...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use it anywhere, anytime, but again: When there is only one block being used it is useless. What about spread
with
with only one block being used on our entire codebase? It will never been anissue
but for sure it will be useless, helping only to increase the code lines.Btw, do you see any advantage declaring:
instead of:
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@filipedelimabrito Unable to access setupActionMenu method without
with(itemView)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
itemView.setupActionMenu(actions_attachment_container)
works.