Commit 05d9453c authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Show groupable messages.

parent d31b9ea1
...@@ -49,7 +49,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView, ...@@ -49,7 +49,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
} }
val messagesViewModels = mapper.mapToViewModelList(messages, settings) val messagesViewModels = mapper.mapToViewModelList(messages, settings)
view.showMessages(messagesViewModels, serverInteractor.get()!!) view.showMessages(messagesViewModels)
// Subscribe after getting the first page of messages from REST // Subscribe after getting the first page of messages from REST
if (offset == 0L) { if (offset == 0L) {
......
...@@ -10,9 +10,8 @@ interface ChatRoomView : LoadingView, MessageView { ...@@ -10,9 +10,8 @@ interface ChatRoomView : LoadingView, MessageView {
* Shows the chat room messages. * Shows the chat room messages.
* *
* @param dataSet The data set to show. * @param dataSet The data set to show.
* @param serverUrl The server URL.
*/ */
fun showMessages(dataSet: List<MessageViewModel>, serverUrl: String) fun showMessages(dataSet: List<MessageViewModel>)
/** /**
* Send a message to a chat room. * Send a message to a chat room.
......
...@@ -3,7 +3,6 @@ package chat.rocket.android.chatroom.ui ...@@ -3,7 +3,6 @@ package chat.rocket.android.chatroom.ui
import android.support.v7.widget.RecyclerView import android.support.v7.widget.RecyclerView
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.chatroom.viewmodel.AttachmentType import chat.rocket.android.chatroom.viewmodel.AttachmentType
...@@ -11,33 +10,33 @@ import chat.rocket.android.chatroom.viewmodel.MessageViewModel ...@@ -11,33 +10,33 @@ import chat.rocket.android.chatroom.viewmodel.MessageViewModel
import chat.rocket.android.player.PlayerActivity import chat.rocket.android.player.PlayerActivity
import chat.rocket.android.util.inflate import chat.rocket.android.util.inflate
import chat.rocket.android.util.setVisible import chat.rocket.android.util.setVisible
import chat.rocket.common.util.ifNull
import com.facebook.drawee.view.SimpleDraweeView import com.facebook.drawee.view.SimpleDraweeView
import com.stfalcon.frescoimageviewer.ImageViewer import com.stfalcon.frescoimageviewer.ImageViewer
import kotlinx.android.synthetic.main.avatar.view.* import kotlinx.android.synthetic.main.avatar.view.*
import kotlinx.android.synthetic.main.item_message.view.* import kotlinx.android.synthetic.main.item_message.view.*
import kotlinx.android.synthetic.main.message_attachment.view.* import kotlinx.android.synthetic.main.message_attachment.view.*
class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() { class ChatRoomAdapter: RecyclerView.Adapter<ChatRoomAdapter.ViewHolder>() {
private val dataSet = ArrayList<MessageViewModel>()
init { init {
setHasStableIds(true) setHasStableIds(true)
} }
val dataSet = ArrayList<MessageViewModel>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ViewHolder(parent.inflate(R.layout.item_message), serverUrl) ViewHolder(parent.inflate(R.layout.item_message))
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(dataSet[position]) override fun onBindViewHolder(holder: ViewHolder, position: Int) =
holder.bind(dataSet[position], dataSet.getOrNull(position + 1))
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>?) { override fun getItemCount(): Int =
onBindViewHolder(holder, position) dataSet.size
}
override fun getItemCount(): Int = dataSet.size override fun getItemViewType(position: Int): Int =
position
override fun getItemViewType(position: Int): Int = position override fun getItemId(position: Int): Long =
dataSet[position].id.hashCode().toLong()
fun addDataSet(dataSet: List<MessageViewModel>) { fun addDataSet(dataSet: List<MessageViewModel>) {
val previousDataSetSize = this.dataSet.size val previousDataSetSize = this.dataSet.size
...@@ -55,22 +54,28 @@ class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<Chat ...@@ -55,22 +54,28 @@ class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<Chat
notifyItemChanged(index) notifyItemChanged(index)
} }
override fun getItemId(position: Int): Long { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
return dataSet[position].id.hashCode().toLong()
}
class ViewHolder(itemView: View, val serverUrl: String) : RecyclerView.ViewHolder(itemView) {
fun bind(message: MessageViewModel) = with(itemView) { fun bind(message: MessageViewModel, nextMessage: MessageViewModel?) = with(itemView) {
bindUserAvatar(message, image_avatar, image_unknown_avatar) image_avatar.setImageURI(message.avatarUri)
text_user_name.text = message.sender text_sender.text = message.senderName
text_message_time.text = message.time text_message_time.text = message.time
text_content.text = message.content
bindAttachment(message, message_attachment, image_attachment, audio_video_attachment, if (nextMessage != null) {
file_name) if (isSequential(message, nextMessage)) {
image_avatar.setVisible(false)
text_sender.setVisible(false)
text_message_time.setVisible(false)
}
}
text_content.text = message.content
bindAttachment(message, message_attachment, image_attachment, audio_video_attachment, file_name)
} }
private fun isSequential(message: MessageViewModel, nextMessage: MessageViewModel): Boolean =
(message.isGroupable && nextMessage.isGroupable) && (message.senderId == nextMessage.senderId)
private fun bindAttachment(message: MessageViewModel, private fun bindAttachment(message: MessageViewModel,
attachment_container: View, attachment_container: View,
image_attachment: SimpleDraweeView, image_attachment: SimpleDraweeView,
...@@ -113,14 +118,5 @@ class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<Chat ...@@ -113,14 +118,5 @@ class ChatRoomAdapter(private val serverUrl: String) : RecyclerView.Adapter<Chat
file_name.text = message.attachmentTitle file_name.text = message.attachmentTitle
} }
} }
private fun bindUserAvatar(message: MessageViewModel, drawee: SimpleDraweeView, imageUnknownAvatar: ImageView) = message.getAvatarUrl(serverUrl).let {
drawee.setImageURI(it.toString())
drawee.setVisible(true)
imageUnknownAvatar.setVisible(false)
}.ifNull {
drawee.setVisible(false)
imageUnknownAvatar.setVisible(true)
}
} }
} }
\ No newline at end of file
...@@ -39,7 +39,6 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type" ...@@ -39,7 +39,6 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only" private const val BUNDLE_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
class ChatRoomFragment : Fragment(), ChatRoomView { class ChatRoomFragment : Fragment(), ChatRoomView {
@Inject lateinit var presenter: ChatRoomPresenter @Inject lateinit var presenter: ChatRoomPresenter
private lateinit var chatRoomId: String private lateinit var chatRoomId: String
private lateinit var chatRoomName: String private lateinit var chatRoomName: String
...@@ -75,10 +74,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView { ...@@ -75,10 +74,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView {
super.onDestroyView() super.onDestroyView()
} }
override fun showMessages(dataSet: List<MessageViewModel>, serverUrl: String) { override fun showMessages(dataSet: List<MessageViewModel>) {
activity?.apply { activity?.apply {
if (recycler_view.adapter == null) { if (recycler_view.adapter == null) {
adapter = ChatRoomAdapter(serverUrl) adapter = ChatRoomAdapter()
recycler_view.adapter = adapter recycler_view.adapter = adapter
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true)
recycler_view.layoutManager = linearLayoutManager recycler_view.layoutManager = linearLayoutManager
......
...@@ -26,22 +26,26 @@ data class MessageViewModel(val context: Context, ...@@ -26,22 +26,26 @@ data class MessageViewModel(val context: Context,
private val message: Message, private val message: Message,
private val settings: Map<String, Value<Any>>?) { private val settings: Map<String, Value<Any>>?) {
val id: String = message.id val id: String = message.id
val isGroupable = message.groupable
val senderId = message.sender?.id
val avatarUri: String?
val time: CharSequence val time: CharSequence
val sender: CharSequence val senderName: CharSequence
val content: CharSequence val content: CharSequence
var attachmentUrl: String? = null var attachmentUrl: String? = null
var attachmentTitle: CharSequence? = null var attachmentTitle: CharSequence? = null
var attachmentType: AttachmentType? = null var attachmentType: AttachmentType? = null
private val baseUrl = settings?.get(SITE_URL)
init { init {
sender = getSenderName() avatarUri = getUserAvatar()
content = getContent(context)
time = getTime() time = getTime()
senderName = getSender()
content = getContent(context)
message.attachments?.let { message.attachments?.let {
if (it.isEmpty() || it[0] == null) return@let if (it.isEmpty()) return@let
val attachment = it[0] as FileAttachment val attachment = it[0] as FileAttachment
val baseUrl = settings?.get(SITE_URL)
baseUrl?.let { baseUrl?.let {
attachmentUrl = attachmentUrl("${baseUrl.value}${attachment.url}") attachmentUrl = attachmentUrl("${baseUrl.value}${attachment.url}")
attachmentTitle = attachment.title attachmentTitle = attachment.title
...@@ -56,36 +60,55 @@ data class MessageViewModel(val context: Context, ...@@ -56,36 +60,55 @@ data class MessageViewModel(val context: Context,
} }
} }
fun getAvatarUrl(serverUrl: String): String? { private fun getUserAvatar(): String? {
return message.sender?.username.let { val username = message.sender?.username ?: "?"
return@let UrlHelper.getAvatarUrl(serverUrl, it.toString()) return baseUrl?.let {
UrlHelper.getAvatarUrl(baseUrl.value.toString(), username)
} }
} }
fun getTime() = DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(message.timestamp)) private fun getTime() =
DateTimeHelper.getTime(DateTimeHelper.getLocalDateTime(message.timestamp))
fun getSenderName(): CharSequence { private fun getSender(): CharSequence {
val useRealName = settings?.get(USE_REALNAME)?.value as Boolean val useRealName = settings?.get(USE_REALNAME)?.value as Boolean
val username = message.sender?.username val username = message.sender?.username
val realName = message.sender?.name val realName = message.sender?.name
val senderName = if (useRealName) realName else username val senderName = if (useRealName) realName else username
return senderName ?: username.toString() return senderName ?: context.getString(R.string.msg_unknown)
} }
fun getContent(context: Context): CharSequence { private fun getContent(context: Context): CharSequence {
val contentMessage: CharSequence val contentMessage: CharSequence
when (message.type) { when (message.type) {
//TODO: Add implementation for Welcome type. //TODO: Add implementation for Welcome type.
is MessageRemoved -> contentMessage = getSystemMessage(context.getString(R.string.message_removed)) is MessageRemoved -> {
is UserJoined -> contentMessage = getSystemMessage(context.getString(R.string.message_user_joined_channel)) contentMessage = getSystemMessage(context.getString(R.string.message_removed))
is UserLeft -> contentMessage = getSystemMessage(context.getString(R.string.message_user_left)) }
is UserAdded -> contentMessage = getSystemMessage( is UserJoined -> {
context.getString(R.string.message_user_added_by, message.message, message.sender?.username)) contentMessage = getSystemMessage(context.getString(R.string.message_user_joined_channel))
is RoomNameChanged -> contentMessage = getSystemMessage( }
context.getString(R.string.message_room_name_changed, message.message, message.sender?.username)) is UserLeft -> {
is UserRemoved -> contentMessage = getSystemMessage( contentMessage = getSystemMessage(context.getString(R.string.message_user_left))
context.getString(R.string.message_user_removed_by, message.message, message.sender?.username)) }
else -> contentMessage = getNormalMessage() is UserAdded -> {
contentMessage = getSystemMessage(context.getString(R.string.message_user_added_by,
message.message,
message.sender?.username))
}
is RoomNameChanged -> {
contentMessage = getSystemMessage(context.getString(R.string.message_room_name_changed,
message.message,
message.sender?.username))
}
is UserRemoved -> {
contentMessage = getSystemMessage(context.getString(R.string.message_user_removed_by,
message.message,
message.sender?.username))
}
else -> {
contentMessage = getNormalMessage()
}
} }
return contentMessage return contentMessage
} }
...@@ -94,10 +117,14 @@ data class MessageViewModel(val context: Context, ...@@ -94,10 +117,14 @@ data class MessageViewModel(val context: Context,
private fun getSystemMessage(content: String): CharSequence { private fun getSystemMessage(content: String): CharSequence {
val spannableMsg = SpannableString(content) val spannableMsg = SpannableString(content)
spannableMsg.setSpan(StyleSpan(Typeface.ITALIC), 0, spannableMsg.length, spannableMsg.setSpan(StyleSpan(Typeface.ITALIC),
0) 0,
spannableMsg.setSpan(ForegroundColorSpan(Color.GRAY), 0, spannableMsg.length, spannableMsg.length,
0) 0)
spannableMsg.setSpan(ForegroundColorSpan(Color.GRAY),
0,
spannableMsg.length,
0)
val username = message.sender?.username val username = message.sender?.username
val message = message.message val message = message.message
...@@ -108,13 +135,17 @@ data class MessageViewModel(val context: Context, ...@@ -108,13 +135,17 @@ data class MessageViewModel(val context: Context,
val messageTextEndIndex = messageTextStartIndex + message.length val messageTextEndIndex = messageTextStartIndex + message.length
if (usernameTextStartIndex > -1) { if (usernameTextStartIndex > -1) {
spannableMsg.setSpan(StyleSpan(Typeface.BOLD_ITALIC), usernameTextStartIndex, usernameTextEndIndex, spannableMsg.setSpan(StyleSpan(Typeface.BOLD_ITALIC),
0) usernameTextStartIndex,
usernameTextEndIndex,
0)
} }
if (messageTextStartIndex > -1) { if (messageTextStartIndex > -1) {
spannableMsg.setSpan(StyleSpan(Typeface.BOLD_ITALIC), messageTextStartIndex, messageTextEndIndex, spannableMsg.setSpan(StyleSpan(Typeface.BOLD_ITALIC),
0) messageTextStartIndex,
messageTextEndIndex,
0)
} }
return spannableMsg return spannableMsg
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
...@@ -12,14 +11,4 @@ ...@@ -12,14 +11,4 @@
android:layout_height="40dp" android:layout_height="40dp"
app:roundedCornerRadius="2dp" /> app:roundedCornerRadius="2dp" />
<!-- TODO define the correct bg color for this-->
<ImageView
android:id="@+id/image_unknown_avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_help_black_24dp"
android:tint="@color/colorAccent"
android:visibility="gone"
tools:ignore="contentDescription" />
</LinearLayout> </LinearLayout>
\ No newline at end of file
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
app:layout_constraintLeft_toRightOf="@+id/layout_avatar"> app:layout_constraintLeft_toRightOf="@+id/layout_avatar">
<TextView <TextView
android:id="@+id/text_user_name" android:id="@+id/text_sender"
style="@style/TextAppearance.AppCompat.Title" style="@style/TextAppearance.AppCompat.Title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment