-
Notifications
You must be signed in to change notification settings - Fork 555
[NEW] Add support for initial Rich Messaging #1557
Changes from 2 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,105 @@ | ||
package chat.rocket.android.chatroom.adapter | ||
|
||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.recyclerview.widget.RecyclerView | ||
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 chat.rocket.android.R | ||
import chat.rocket.android.util.TimberLogger | ||
import com.facebook.drawee.backends.pipeline.Fresco | ||
import com.facebook.drawee.view.SimpleDraweeView | ||
import com.google.android.material.button.MaterialButton | ||
|
||
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 | ||
TimberLogger.debug("no of actions : ${actions.size} : $actions") | ||
with(itemView) { | ||
title.text = data.title ?: "" | ||
actions_list.layoutManager = LinearLayoutManager(itemView.context) | ||
actions_list.adapter = ActionsListAdapter(actions, actionAttachmentOnClickListener) | ||
} | ||
} | ||
} | ||
|
||
interface ActionAttachmentOnClickListener { | ||
fun onActionClicked(action: Action) | ||
} | ||
|
||
class ActionsListAdapter(actions: List<Action>, var actionAttachmentOnClickListener: ActionAttachmentOnClickListener) : RecyclerView.Adapter<ActionsListAdapter.ViewHolder>() { | ||
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. I think I'd prefer using a new package and multiple files instead of multiple classes and inner classes on the same file |
||
|
||
var actions: List<Action> = actions | ||
|
||
inner class ViewHolder(var layout: View) : RecyclerView.ViewHolder(layout) { | ||
lateinit var action: ButtonAction | ||
|
||
var button: MaterialButton = layout.findViewById(R.id.action_button) | ||
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. No need for findView, can use |
||
val image: SimpleDraweeView = layout.findViewById(R.id.action_image_button) | ||
|
||
private val onClickListener = View.OnClickListener { | ||
actionAttachmentOnClickListener.onActionClicked(action) | ||
} | ||
|
||
init { | ||
button.setOnClickListener(onClickListener) | ||
image.setOnClickListener(onClickListener) | ||
} | ||
|
||
fun bindAction(action: Action) { | ||
TimberLogger.debug("action : $action") | ||
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. Don't use 'TimberLogger' directly, use |
||
this.action = action as ButtonAction | ||
|
||
//TODO | ||
if (action.imageUrl != null) { | ||
button.visibility = View.GONE | ||
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. Replace with |
||
image.visibility = View.VISIBLE | ||
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. Replace with |
||
|
||
//Image button | ||
val controller = Fresco.newDraweeControllerBuilder().apply { | ||
setUri(action.imageUrl) | ||
autoPlayAnimations = true | ||
oldController = image.controller | ||
}.build() | ||
image.controller = controller | ||
|
||
} else if (action.text != null) { | ||
button.visibility = View.VISIBLE | ||
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. Replace with |
||
image.visibility = View.GONE | ||
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. Replace with |
||
|
||
this.button.setText(action.text) | ||
} | ||
} | ||
} | ||
|
||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { | ||
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_action_button, parent, false) | ||
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,28 @@ 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.ViewGroup | ||
import android.widget.Toast | ||
import androidx.browser.customtabs.CustomTabsIntent | ||
import androidx.core.content.res.ResourcesCompat | ||
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 presenter: ChatRoomPresenter? = null, | ||
|
@@ -73,6 +81,10 @@ class ChatRoomAdapter( | |
presenter?.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()}") | ||
} | ||
|
@@ -126,6 +138,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) | ||
} | ||
} | ||
|
||
|
@@ -204,6 +218,42 @@ class ChatRoomAdapter( | |
} | ||
} | ||
|
||
private val actionAttachmentOnClickListener = object : ActionAttachmentOnClickListener { | ||
override fun onActionClicked(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 |
||
//Open in a configurable sizable webview | ||
Toast.makeText(context, "Open in a configurable sizable webview", Toast.LENGTH_SHORT).show() | ||
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. Hard coded string. Please, remove and use the string from the |
||
} else { | ||
//Open in chrome custom tab | ||
with(this) { | ||
val customTabsBuilder = CustomTabsIntent.Builder() | ||
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. we have a |
||
customTabsBuilder.setToolbarColor(ResourcesCompat.getColor(context!!.resources, R.color.colorPrimary, context.theme)) | ||
val customTabsIntent = customTabsBuilder.build() | ||
try { | ||
customTabsIntent.launchUrl(context, Uri.parse(temp.url!!)) | ||
} catch (ex: Exception) { | ||
Timber.d(ex, "Unable to launch 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 { | ||
//Send to bot but not in chat window | ||
Toast.makeText(context, "Send to bot but not in chat window", Toast.LENGTH_SHORT).show() | ||
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. Hard coded string. Please, remove and use the string from the |
||
} | ||
} | ||
} | ||
} | ||
|
||
private val actionsListener = object : BaseViewHolder.ActionsListener { | ||
|
||
override fun isActionsEnabled(): Boolean = enableActions | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -336,6 +336,7 @@ 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, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
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>, | ||
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,25 @@ | ||
<?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 | ||
style="@style/Widget.MaterialComponents.Button" | ||
android:id="@+id/action_button" | ||
android:textSize="12sp" | ||
android:textAppearance="@style/TextAppearance.AppCompat.Body2" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" /> | ||
|
||
<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: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.