Move message holders to native view bindings

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
This commit is contained in:
Andy Scherzinger 2021-06-10 16:40:55 +02:00
parent 71bffc0ea8
commit 6ba541b6b1
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
12 changed files with 256 additions and 339 deletions

View file

@ -3,8 +3,10 @@
*
* @author Mario Danic
* @author Marcel Hibbe
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
* @author Andy Scherzinger
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,22 +37,16 @@ import android.view.MotionEvent
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.ViewCompat
import androidx.emoji.widget.EmojiTextView
import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import coil.load
import com.amulyakhare.textdrawable.TextDrawable
import com.facebook.drawee.view.SimpleDraweeView
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.databinding.ItemCustomIncomingLocationMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils
@ -62,6 +58,7 @@ import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
.IncomingTextMessageViewHolder<ChatMessage>(incomingView) {
private val binding: ItemCustomIncomingLocationMessageBinding = ItemCustomIncomingLocationMessageBinding.bind(itemView)
private val TAG = "LocMessageView"
@ -70,42 +67,6 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
var locationName: String? = ""
var locationGeoLink: String? = ""
@JvmField
@BindView(R.id.messageAuthor)
var messageAuthor: EmojiTextView? = null
@JvmField
@BindView(R.id.messageText)
var messageText: EmojiTextView? = null
@JvmField
@BindView(R.id.messageUserAvatar)
var messageUserAvatarView: SimpleDraweeView? = null
@JvmField
@BindView(R.id.messageTime)
var messageTimeView: TextView? = null
@JvmField
@BindView(R.id.quotedChatMessageView)
var quotedChatMessageView: RelativeLayout? = null
@JvmField
@BindView(R.id.quotedMessageAuthor)
var quotedUserName: EmojiTextView? = null
@JvmField
@BindView(R.id.quotedMessageImage)
var quotedMessagePreview: ImageView? = null
@JvmField
@BindView(R.id.quotedMessage)
var quotedMessage: EmojiTextView? = null
@JvmField
@BindView(R.id.quoteColoredView)
var quoteColoredView: View? = null
@JvmField
@Inject
var context: Context? = null
@ -114,30 +75,19 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
@Inject
var appPreferences: AppPreferences? = null
@JvmField
@BindView(R.id.webview)
var webview: WebView? = null
init {
ButterKnife.bind(
this,
itemView
)
}
@SuppressLint("SetTextI18n", "SetJavaScriptEnabled", "ClickableViewAccessibility")
override fun onBind(message: ChatMessage) {
super.onBind(message)
sharedApplication!!.componentApplication.inject(this)
val author: String = message.actorDisplayName
if (!TextUtils.isEmpty(author)) {
messageAuthor!!.text = author
binding.messageAuthor.text = author
} else {
messageAuthor!!.setText(R.string.nc_nick_guest)
binding.messageAuthor.setText(R.string.nc_nick_guest)
}
if (!message.isGrouped && !message.isOneToOneConversation) {
messageUserAvatarView!!.visibility = View.VISIBLE
binding.messageUserAvatar.visibility = View.VISIBLE
if (message.actorType == "guests") {
// do nothing, avatar is set
} else if (message.actorType == "bots" && message.actorId == "changelog") {
@ -145,7 +95,7 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
layers[0] = AppCompatResources.getDrawable(context!!, R.drawable.ic_launcher_background)
layers[1] = AppCompatResources.getDrawable(context!!, R.drawable.ic_launcher_foreground)
val layerDrawable = LayerDrawable(layers)
messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
} else if (message.actorType == "bots") {
val drawable = TextDrawable.builder()
.beginConfig()
@ -155,16 +105,16 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
">",
context!!.resources.getColor(R.color.black)
)
messageUserAvatarView!!.visibility = View.VISIBLE
messageUserAvatarView?.setImageDrawable(drawable)
binding.messageUserAvatar.visibility = View.VISIBLE
binding.messageUserAvatar.setImageDrawable(drawable)
}
} else {
if (message.isOneToOneConversation) {
messageUserAvatarView!!.visibility = View.GONE
binding.messageUserAvatar.visibility = View.GONE
} else {
messageUserAvatarView!!.visibility = View.INVISIBLE
binding.messageUserAvatar.visibility = View.INVISIBLE
}
messageAuthor!!.visibility = View.GONE
binding.messageAuthor.visibility = View.GONE
}
val resources = itemView.resources
@ -188,47 +138,45 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
)
ViewCompat.setBackground(bubble, bubbleDrawable)
val messageParameters = message.messageParameters
itemView.isSelected = false
messageTimeView!!.setTextColor(context?.resources!!.getColor(R.color.warm_grey_four))
binding.messageTime.setTextColor(context?.resources!!.getColor(R.color.warm_grey_four))
val textSize = context?.resources!!.getDimension(R.dimen.chat_text_size)
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
messageText!!.text = message.text
messageText!!.isEnabled = false
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageText.text = message.text
binding.messageText.isEnabled = false
// parent message handling
if (!message.isDeleted && message.parentMessage != null) {
val parentChatMessage = message.parentMessage
parentChatMessage.activeUser = message.activeUser
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
binding.messageQuote.quotedMessageImage.load(it) {
addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
binding.messageQuote.quotedMessageImage.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
binding.messageQuote.quotedMessage.text = parentChatMessage.text
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
binding.messageQuote.quotedMessageAuthor
.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
if (parentChatMessage.actorId?.equals(message.activeUser.userId) == true) {
quoteColoredView?.setBackgroundResource(R.color.colorPrimary)
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.colorPrimary)
} else {
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.textColorMaxContrast)
}
quotedChatMessageView?.visibility = View.VISIBLE
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
quotedChatMessageView?.visibility = View.GONE
binding.messageQuote.quotedChatMessageView.visibility = View.GONE
}
// geo-location
@ -245,9 +193,9 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
}
}
webview?.settings?.javaScriptEnabled = true
binding.webview.settings?.javaScriptEnabled = true
webview?.webViewClient = object : WebViewClient() {
binding.webview.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return if (url != null && (url.startsWith("http://") || url.startsWith("https://"))
) {
@ -268,9 +216,9 @@ class IncomingLocationMessageViewHolder(incomingView: View) : MessageHolders
urlStringBuffer.append("&locationName=" + URLEncoder.encode(locationName))
urlStringBuffer.append("&locationGeoLink=" + URLEncoder.encode(locationGeoLink))
webview?.loadUrl(urlStringBuffer.toString())
binding.webview.loadUrl(urlStringBuffer.toString())
webview?.setOnTouchListener(object : View.OnTouchListener {
binding.webview.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_UP -> openGeoLink()

View file

@ -0,0 +1,45 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.adapters.messages;
import android.view.View;
import android.widget.ProgressBar;
import com.nextcloud.talk.databinding.ItemCustomIncomingPreviewMessageBinding;
import androidx.emoji.widget.EmojiTextView;
public class IncomingPreviewMessageViewHolder extends MagicPreviewMessageViewHolder {
private final ItemCustomIncomingPreviewMessageBinding binding;
public IncomingPreviewMessageViewHolder(View itemView) {
super(itemView);
binding = ItemCustomIncomingPreviewMessageBinding.bind(itemView);
}
public EmojiTextView getMessageText() {
return binding.messageText;
}
public ProgressBar getProgressBar() {
return binding.progressBar;
}
}

View file

@ -2,6 +2,8 @@
* Nextcloud Talk application
*
* @author Mario Danic
* @author Andy Scherzinger
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
@ -30,20 +32,14 @@ import android.text.SpannableString
import android.text.TextUtils
import android.util.TypedValue
import android.view.View
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.emoji.widget.EmojiTextView
import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import coil.load
import com.amulyakhare.textdrawable.TextDrawable
import com.facebook.drawee.view.SimpleDraweeView
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.databinding.ItemCustomIncomingTextMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
import com.nextcloud.talk.utils.ApiUtils
@ -57,41 +53,7 @@ import javax.inject.Inject
class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
.IncomingTextMessageViewHolder<ChatMessage>(itemView) {
@JvmField
@BindView(R.id.messageAuthor)
var messageAuthor: EmojiTextView? = null
@JvmField
@BindView(R.id.messageText)
var messageText: EmojiTextView? = null
@JvmField
@BindView(R.id.messageUserAvatar)
var messageUserAvatarView: SimpleDraweeView? = null
@JvmField
@BindView(R.id.messageTime)
var messageTimeView: TextView? = null
@JvmField
@BindView(R.id.quotedChatMessageView)
var quotedChatMessageView: RelativeLayout? = null
@JvmField
@BindView(R.id.quotedMessageAuthor)
var quotedUserName: EmojiTextView? = null
@JvmField
@BindView(R.id.quotedMessageImage)
var quotedMessagePreview: ImageView? = null
@JvmField
@BindView(R.id.quotedMessage)
var quotedMessage: EmojiTextView? = null
@JvmField
@BindView(R.id.quoteColoredView)
var quoteColoredView: View? = null
private val binding: ItemCustomIncomingTextMessageBinding = ItemCustomIncomingTextMessageBinding.bind(itemView)
@JvmField
@Inject
@ -106,13 +68,13 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
sharedApplication!!.componentApplication.inject(this)
val author: String = message.actorDisplayName
if (!TextUtils.isEmpty(author)) {
messageAuthor!!.text = author
binding.messageAuthor.text = author
} else {
messageAuthor!!.setText(R.string.nc_nick_guest)
binding.messageAuthor.setText(R.string.nc_nick_guest)
}
if (!message.isGrouped && !message.isOneToOneConversation) {
messageUserAvatarView!!.visibility = View.VISIBLE
binding.messageUserAvatar.visibility = View.VISIBLE
if (message.actorType == "guests") {
// do nothing, avatar is set
} else if (message.actorType == "bots" && message.actorId == "changelog") {
@ -120,7 +82,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
layers[0] = context?.getDrawable(R.drawable.ic_launcher_background)
layers[1] = context?.getDrawable(R.drawable.ic_launcher_foreground)
val layerDrawable = LayerDrawable(layers)
messageUserAvatarView?.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
binding.messageUserAvatar.setImageDrawable(DisplayUtils.getRoundedDrawable(layerDrawable))
} else if (message.actorType == "bots") {
val drawable = TextDrawable.builder()
.beginConfig()
@ -130,16 +92,16 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
">",
context!!.resources.getColor(R.color.black)
)
messageUserAvatarView!!.visibility = View.VISIBLE
messageUserAvatarView?.setImageDrawable(drawable)
binding.messageUserAvatar.visibility = View.VISIBLE
binding.messageUserAvatar.setImageDrawable(drawable)
}
} else {
if (message.isOneToOneConversation) {
messageUserAvatarView!!.visibility = View.GONE
binding.messageUserAvatar.visibility = View.GONE
} else {
messageUserAvatarView!!.visibility = View.INVISIBLE
binding.messageUserAvatar.visibility = View.INVISIBLE
}
messageAuthor!!.visibility = View.GONE
binding.messageAuthor.visibility = View.GONE
}
val resources = itemView.resources
@ -166,7 +128,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
val messageParameters = message.messageParameters
itemView.isSelected = false
messageTimeView!!.setTextColor(context?.resources!!.getColor(R.color.warm_grey_four))
binding.messageTime.setTextColor(context?.resources!!.getColor(R.color.warm_grey_four))
var messageString: Spannable = SpannableString(message.text)
@ -183,7 +145,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
) {
if (individualHashMap["id"] == message.activeUser!!.userId) {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
messageText!!.context,
binding.messageText.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
@ -193,7 +155,7 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
)
} else {
messageString = DisplayUtils.searchAndReplaceWithMentionSpan(
messageText!!.context,
binding.messageText.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
@ -213,49 +175,45 @@ class MagicIncomingTextMessageViewHolder(itemView: View) : MessageHolders
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
textSize = (textSize * 2.5).toFloat()
itemView.isSelected = true
messageAuthor!!.visibility = View.GONE
binding.messageAuthor.visibility = View.GONE
}
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
messageText!!.text = messageString
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageText.text = messageString
// parent message handling
if (!message.isDeleted && message.parentMessage != null) {
var parentChatMessage = message.parentMessage
val parentChatMessage = message.parentMessage
parentChatMessage.activeUser = message.activeUser
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
binding.messageQuote.quotedMessageImage.load(it) {
addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
binding.messageQuote.quotedMessageImage.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
binding.messageQuote.quotedMessage.text = parentChatMessage.text
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
binding.messageQuote.quotedMessageAuthor
.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast))
if (parentChatMessage.actorId?.equals(message.activeUser.userId) == true) {
quoteColoredView?.setBackgroundResource(R.color.colorPrimary)
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.colorPrimary)
} else {
quoteColoredView?.setBackgroundResource(R.color.textColorMaxContrast)
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.textColorMaxContrast)
}
quotedChatMessageView?.visibility = View.VISIBLE
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
quotedChatMessageView?.visibility = View.GONE
binding.messageQuote.quotedChatMessageView.visibility = View.GONE
}
itemView.setTag(MessageSwipeCallback.REPLYABLE_VIEW_TAG, message.isReplyable)
}
init {
ButterKnife.bind(this, itemView)
}
}

View file

@ -2,6 +2,8 @@
* Nextcloud Talk application
*
* @author Mario Danic
* @author Andy Scherzinger
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
@ -27,19 +29,14 @@ import android.text.Spannable
import android.text.SpannableString
import android.util.TypedValue
import android.view.View
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.core.view.ViewCompat
import androidx.emoji.widget.EmojiTextView
import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import coil.load
import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.databinding.ItemCustomOutcomingTextMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback
@ -53,57 +50,21 @@ import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewHolder<ChatMessage>(itemView) {
@JvmField
@BindView(R.id.messageText)
var messageText: EmojiTextView? = null
@JvmField
@BindView(R.id.messageTime)
var messageTimeView: TextView? = null
@JvmField
@BindView(R.id.quotedChatMessageView)
var quotedChatMessageView: RelativeLayout? = null
@JvmField
@BindView(R.id.quotedMessageAuthor)
var quotedUserName: EmojiTextView? = null
@JvmField
@BindView(R.id.quotedMessageImage)
var quotedMessagePreview: ImageView? = null
@JvmField
@BindView(R.id.quotedMessage)
var quotedMessage: EmojiTextView? = null
@JvmField
@BindView(R.id.quoteColoredView)
var quoteColoredView: View? = null
@JvmField
@BindView(R.id.checkMark)
var checkMark: ImageView? = null
private val binding: ItemCustomOutcomingTextMessageBinding = ItemCustomOutcomingTextMessageBinding.bind(itemView)
private val realView: View = itemView
@JvmField
@Inject
var context: Context? = null
private val realView: View
init {
ButterKnife.bind(this, itemView)
this.realView = itemView
}
override fun onBind(message: ChatMessage) {
super.onBind(message)
sharedApplication!!.componentApplication.inject(this)
val messageParameters: HashMap<String, HashMap<String, String>>? = message.messageParameters
var messageString: Spannable = SpannableString(message.text)
realView.isSelected = false
messageTimeView!!.setTextColor(context!!.resources.getColor(R.color.white60))
val layoutParams = messageTimeView!!.layoutParams as FlexboxLayout.LayoutParams
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60))
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
layoutParams.isWrapBefore = false
var textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
if (messageParameters != null && messageParameters.size > 0) {
@ -115,7 +76,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
) || individualHashMap["type"] == "call"
) {
messageString = searchAndReplaceWithMentionSpan(
messageText!!.context,
binding.messageText.context,
messageString,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
@ -136,7 +97,7 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
textSize = (textSize * 2.5).toFloat()
layoutParams.isWrapBefore = true
messageTimeView!!.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.warm_grey_four))
realView.isSelected = true
}
val resources = sharedApplication!!.resources
@ -162,9 +123,9 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
)
ViewCompat.setBackground(bubble, bubbleDrawable)
}
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
messageTimeView!!.layoutParams = layoutParams
messageText!!.text = messageString
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageTime.layoutParams = layoutParams
binding.messageText.text = messageString
// parent message handling
@ -172,27 +133,27 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
var parentChatMessage = message.parentMessage
parentChatMessage.activeUser = message.activeUser
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
binding.messageQuote.quotedMessageImage.load(it) {
addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
binding.messageQuote.quotedMessageImage.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
quotedMessage?.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.nc_grey))
binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
binding.messageQuote.quotedMessageAuthor.setTextColor(context!!.resources.getColor(R.color.nc_grey))
quoteColoredView?.setBackgroundResource(R.color.white)
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white)
quotedChatMessageView?.visibility = View.VISIBLE
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
quotedChatMessageView?.visibility = View.GONE
binding.messageQuote.quotedChatMessageView.visibility = View.GONE
}
val readStatusDrawableInt = when (message.readStatus) {
@ -210,11 +171,11 @@ class MagicOutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessage
readStatusDrawableInt?.let { drawableInt ->
context?.resources?.getDrawable(drawableInt, null)?.let {
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
checkMark?.setImageDrawable(it)
binding.checkMark.setImageDrawable(it)
}
}
checkMark?.setContentDescription(readStatusContentDescriptionString)
binding.checkMark.setContentDescription(readStatusContentDescriptionString)
itemView.setTag(MessageSwipeCallback.REPLYABLE_VIEW_TAG, message.isReplyable)
}

View file

@ -3,8 +3,10 @@
*
* @author Mario Danic
* @author Marcel Hibbe
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
* @author Andy Scherzinger
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
* Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
* Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,6 +37,7 @@ import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import com.google.common.util.concurrent.ListenableFuture;
import com.nextcloud.talk.R;
@ -45,6 +48,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.components.filebrowser.models.BrowserFile;
import com.nextcloud.talk.components.filebrowser.models.DavResponse;
import com.nextcloud.talk.components.filebrowser.webdav.ReadFilesystemOperation;
import com.nextcloud.talk.databinding.ItemCustomIncomingPreviewMessageBinding;
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker;
import com.nextcloud.talk.models.database.CapabilitiesUtil;
import com.nextcloud.talk.models.database.UserEntity;
@ -70,8 +74,6 @@ import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import autodagger.AutoInjector;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.annotations.NonNull;
@ -82,15 +84,10 @@ import okhttp3.OkHttpClient;
import static com.nextcloud.talk.ui.recyclerview.MessageSwipeCallback.REPLYABLE_VIEW_TAG;
@AutoInjector(NextcloudTalkApplication.class)
public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageMessageViewHolder<ChatMessage> {
public abstract class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageMessageViewHolder<ChatMessage> {
private static final String TAG = "PreviewMsgViewHolder";
@BindView(R.id.messageText)
EmojiTextView messageText;
View progressBar;
@Inject
Context context;
@ -99,8 +96,6 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
public MagicPreviewMessageViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
progressBar = itemView.findViewById(R.id.progress_bar);
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
}
@ -131,7 +126,7 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
if (message.getMessageType() == ChatMessage.MessageType.SINGLE_NC_ATTACHMENT_MESSAGE) {
String fileName = message.getSelectedIndividualHashMap().get("name");
messageText.setText(fileName);
getMessageText().setText(fileName);
if (message.getSelectedIndividualHashMap().containsKey("mimetype")) {
String mimetype = message.getSelectedIndividualHashMap().get("mimetype");
int drawableResourceId = DrawableUtils.INSTANCE.getDrawableResourceIdForMimeType(mimetype);
@ -165,7 +160,7 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
try {
for (WorkInfo workInfo : workers.get()) {
if (workInfo.getState() == WorkInfo.State.RUNNING || workInfo.getState() == WorkInfo.State.ENQUEUED) {
progressBar.setVisibility(View.VISIBLE);
getProgressBar().setVisibility(View.VISIBLE);
String mimetype = message.getSelectedIndividualHashMap().get("mimetype");
@ -178,11 +173,11 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
Log.e(TAG, "Error when checking if worker already exists", e);
}
} else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_GIPHY_MESSAGE) {
messageText.setText("GIPHY");
DisplayUtils.setClickableString("GIPHY", "https://giphy.com", messageText);
getMessageText().setText("GIPHY");
DisplayUtils.setClickableString("GIPHY", "https://giphy.com", getMessageText());
} else if (message.getMessageType() == ChatMessage.MessageType.SINGLE_LINK_TENOR_MESSAGE) {
messageText.setText("Tenor");
DisplayUtils.setClickableString("Tenor", "https://tenor.com", messageText);
getMessageText().setText("Tenor");
DisplayUtils.setClickableString("Tenor", "https://tenor.com", getMessageText());
} else {
if (message.getMessageType().equals(ChatMessage.MessageType.SINGLE_LINK_IMAGE_MESSAGE)) {
image.setOnClickListener(v -> {
@ -193,12 +188,16 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
} else {
image.setOnClickListener(null);
}
messageText.setText("");
getMessageText().setText("");
}
itemView.setTag(REPLYABLE_VIEW_TAG, message.isReplyable());
}
public abstract EmojiTextView getMessageText();
public abstract ProgressBar getProgressBar();
private void openOrDownloadFile(ChatMessage message) {
String filename = message.getSelectedIndividualHashMap().get("name");
String mimetype = message.getSelectedIndividualHashMap().get("mimetype");
@ -388,7 +387,7 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
WorkManager.getInstance().enqueue(downloadWorker);
progressBar.setVisibility(View.VISIBLE);
getProgressBar().setVisibility(View.VISIBLE);
WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.getId()).observeForever(workInfo -> {
updateViewsByProgress(fileName, mimetype, workInfo);
@ -400,7 +399,7 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
case RUNNING:
int progress = workInfo.getProgress().getInt(DownloadFileToCacheWorker.PROGRESS, -1);
if (progress > -1) {
messageText.setText(String.format(context.getResources().getString(R.string.filename_progress), fileName, progress));
getMessageText().setText(String.format(context.getResources().getString(R.string.filename_progress), fileName, progress));
}
break;
@ -411,13 +410,13 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
Log.d(TAG, "file " + fileName + " was downloaded but it's not opened because view is not shown on" +
" screen");
}
messageText.setText(fileName);
progressBar.setVisibility(View.GONE);
getMessageText().setText(fileName);
getProgressBar().setVisibility(View.GONE);
break;
case FAILED:
messageText.setText(fileName);
progressBar.setVisibility(View.GONE);
getMessageText().setText(fileName);
getProgressBar().setVisibility(View.GONE);
break;
default:
// do nothing
@ -486,6 +485,5 @@ public class MagicPreviewMessageViewHolder extends MessageHolders.IncomingImageM
Log.e(TAG, "Error reading file information", e);
}
});
}
}

View file

@ -33,21 +33,16 @@ import android.view.MotionEvent
import android.view.View
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.ViewCompat
import androidx.emoji.widget.EmojiTextView
import autodagger.AutoInjector
import butterknife.BindView
import butterknife.ButterKnife
import coil.load
import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.databinding.ItemCustomOutcomingLocationMessageBinding
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.utils.ApiUtils
@ -59,6 +54,9 @@ import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
.OutcomingTextMessageViewHolder<ChatMessage>(incomingView) {
private val binding: ItemCustomOutcomingLocationMessageBinding =
ItemCustomOutcomingLocationMessageBinding.bind(itemView)
private val realView: View = itemView
private val TAG = "LocMessageView"
@ -67,56 +65,18 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
var locationName: String? = ""
var locationGeoLink: String? = ""
@JvmField
@BindView(R.id.messageText)
var messageText: EmojiTextView? = null
@JvmField
@BindView(R.id.messageTime)
var messageTimeView: TextView? = null
@JvmField
@BindView(R.id.quotedChatMessageView)
var quotedChatMessageView: RelativeLayout? = null
@JvmField
@BindView(R.id.quotedMessageAuthor)
var quotedUserName: EmojiTextView? = null
@JvmField
@BindView(R.id.quotedMessageImage)
var quotedMessagePreview: ImageView? = null
@JvmField
@BindView(R.id.quotedMessage)
var quotedMessage: EmojiTextView? = null
@JvmField
@BindView(R.id.quoteColoredView)
var quoteColoredView: View? = null
@JvmField
@BindView(R.id.checkMark)
var checkMark: ImageView? = null
@JvmField
@Inject
var context: Context? = null
@JvmField
@BindView(R.id.webview)
var webview: WebView? = null
private val realView: View
@SuppressLint("SetTextI18n", "SetJavaScriptEnabled", "ClickableViewAccessibility")
override fun onBind(message: ChatMessage) {
super.onBind(message)
sharedApplication!!.componentApplication.inject(this)
realView.isSelected = false
messageTimeView!!.setTextColor(context!!.resources.getColor(R.color.white60))
val layoutParams = messageTimeView!!.layoutParams as FlexboxLayout.LayoutParams
binding.messageTime.setTextColor(context!!.resources.getColor(R.color.white60))
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
layoutParams.isWrapBefore = false
val textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
@ -144,10 +104,10 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
)
ViewCompat.setBackground(bubble, bubbleDrawable)
}
messageText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
messageTimeView!!.layoutParams = layoutParams
messageText!!.text = message.text
messageText!!.isEnabled = false
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageTime.layoutParams = layoutParams
binding.messageText.text = message.text
binding.messageText.isEnabled = false
// parent message handling
@ -155,27 +115,27 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
val parentChatMessage = message.parentMessage
parentChatMessage.activeUser = message.activeUser
parentChatMessage.imageUrl?.let {
quotedMessagePreview?.visibility = View.VISIBLE
quotedMessagePreview?.load(it) {
binding.messageQuote.quotedMessageImage.visibility = View.VISIBLE
binding.messageQuote.quotedMessageImage.load(it) {
addHeader(
"Authorization",
ApiUtils.getCredentials(message.activeUser.username, message.activeUser.token)
)
}
} ?: run {
quotedMessagePreview?.visibility = View.GONE
binding.messageQuote.quotedMessageImage.visibility = View.GONE
}
quotedUserName?.text = parentChatMessage.actorDisplayName
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest)
quotedMessage?.text = parentChatMessage.text
quotedMessage?.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
quotedUserName?.setTextColor(context!!.resources.getColor(R.color.nc_grey))
binding.messageQuote.quotedMessage.text = parentChatMessage.text
binding.messageQuote.quotedMessage.setTextColor(context!!.resources.getColor(R.color.nc_outcoming_text_default))
binding.messageQuote.quotedMessageAuthor.setTextColor(context!!.resources.getColor(R.color.nc_grey))
quoteColoredView?.setBackgroundResource(R.color.white)
binding.messageQuote.quoteColoredView.setBackgroundResource(R.color.white)
quotedChatMessageView?.visibility = View.VISIBLE
binding.messageQuote.quotedChatMessageView.visibility = View.VISIBLE
} else {
quotedChatMessageView?.visibility = View.GONE
binding.messageQuote.quotedChatMessageView.visibility = View.GONE
}
val readStatusDrawableInt = when (message.readStatus) {
@ -193,11 +153,11 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
readStatusDrawableInt?.let { drawableInt ->
AppCompatResources.getDrawable(context!!, drawableInt)?.let {
it.setColorFilter(context?.resources!!.getColor(R.color.white60), PorterDuff.Mode.SRC_ATOP)
checkMark?.setImageDrawable(it)
binding.checkMark.setImageDrawable(it)
}
}
checkMark?.setContentDescription(readStatusContentDescriptionString)
binding.checkMark.setContentDescription(readStatusContentDescriptionString)
// geo-location
@ -213,9 +173,9 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
}
}
webview?.settings?.javaScriptEnabled = true
binding.webview.settings?.javaScriptEnabled = true
webview?.webViewClient = object : WebViewClient() {
binding.webview.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return if (url != null && (url.startsWith("http://") || url.startsWith("https://"))
) {
@ -242,9 +202,9 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
urlStringBuffer.append("&locationName=" + URLEncoder.encode(locationName))
urlStringBuffer.append("&locationGeoLink=" + URLEncoder.encode(locationGeoLink))
webview?.loadUrl(urlStringBuffer.toString())
binding.webview.loadUrl(urlStringBuffer.toString())
webview?.setOnTouchListener(object : View.OnTouchListener {
binding.webview.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_UP -> openGeoLink()
@ -270,9 +230,4 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : MessageHolders
private fun addMarkerToGeoLink(locationGeoLink: String): String {
return locationGeoLink.replace("geo:", "geo:0,0?q=")
}
init {
ButterKnife.bind(this, itemView)
this.realView = itemView
}
}

View file

@ -0,0 +1,45 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.adapters.messages;
import android.view.View;
import android.widget.ProgressBar;
import com.nextcloud.talk.databinding.ItemCustomOutcomingPreviewMessageBinding;
import androidx.emoji.widget.EmojiTextView;
public class OutcomingPreviewMessageViewHolder extends MagicPreviewMessageViewHolder {
private final ItemCustomOutcomingPreviewMessageBinding binding;
public OutcomingPreviewMessageViewHolder(View itemView) {
super(itemView);
binding = ItemCustomOutcomingPreviewMessageBinding.bind(itemView);
}
public EmojiTextView getMessageText() {
return binding.messageText;
}
public ProgressBar getProgressBar() {
return binding.progressBar;
}
}

View file

@ -79,12 +79,13 @@ import com.google.android.flexbox.FlexboxLayout
import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.adapters.messages.IncomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.IncomingPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicIncomingTextMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicOutcomingTextMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicSystemMessageViewHolder
import com.nextcloud.talk.adapters.messages.MagicUnreadNoticeMessageViewHolder
import com.nextcloud.talk.adapters.messages.OutcomingLocationMessageViewHolder
import com.nextcloud.talk.adapters.messages.OutcomingPreviewMessageViewHolder
import com.nextcloud.talk.adapters.messages.TalkMessagesListAdapter
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
@ -400,23 +401,15 @@ class ChatController(args: Bundle) :
)
messageHolders.setIncomingImageConfig(
MagicPreviewMessageViewHolder::class.java,
IncomingPreviewMessageViewHolder::class.java,
R.layout.item_custom_incoming_preview_message
)
messageHolders.setOutcomingImageConfig(
MagicPreviewMessageViewHolder::class.java,
OutcomingPreviewMessageViewHolder::class.java,
R.layout.item_custom_outcoming_preview_message
)
// messageHolders.setIncomingLocationConfig(
// LocationMessageViewHolder::class.java,
// R.layout.item_custom_location_message
// )
// messageHolders.setOutcomingLocationConfig(
// LocationMessageViewHolder::class.java,
// R.layout.item_custom_location_message
// )
messageHolders.registerContentType(
CONTENT_TYPE_SYSTEM_MESSAGE,
MagicSystemMessageViewHolder::class.java,

View file

@ -3,8 +3,10 @@
~
~ @author Mario Danic
~ @author Marcel Hibbe
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~ @author Andy Scherzinger
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
@ -49,13 +51,15 @@
app:flexWrap="wrap"
app:justifyContent="flex_end">
<include layout="@layout/item_message_quote" android:visibility="gone"/>
<include
android:id="@+id/message_quote"
layout="@layout/item_message_quote"
android:visibility="gone" />
<WebView
android:id="@+id/webview"
android:layout_width="400dp"
android:layout_height="200dp"
/>
android:layout_height="200dp" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/messageAuthor"

View file

@ -2,6 +2,8 @@
~ Nextcloud Talk application
~
~ @author Mario Danic
~ @author Andy Scherzinger
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2017-2018 Mario Danic <mario@lovelyhq.com>
~
~ This program is free software: you can redistribute it and/or modify
@ -47,7 +49,10 @@
app:flexWrap="wrap"
app:justifyContent="flex_end">
<include layout="@layout/item_message_quote" android:visibility="gone"/>
<include
android:id="@+id/message_quote"
layout="@layout/item_message_quote"
android:visibility="gone" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/messageAuthor"

View file

@ -41,13 +41,15 @@
app:flexWrap="wrap"
app:justifyContent="flex_end">
<include layout="@layout/item_message_quote" android:visibility="gone"/>
<include
android:id="@+id/message_quote"
layout="@layout/item_message_quote"
android:visibility="gone" />
<WebView
android:id="@+id/webview"
android:layout_width="400dp"
android:layout_height="200dp"
/>
android:layout_height="200dp" />
<androidx.emoji.widget.EmojiTextView
android:id="@id/messageText"

View file

@ -41,7 +41,10 @@
app:flexWrap="wrap"
app:justifyContent="flex_end">
<include layout="@layout/item_message_quote" android:visibility="gone"/>
<include
android:id="@+id/message_quote"
layout="@layout/item_message_quote"
android:visibility="gone" />
<androidx.emoji.widget.EmojiTextView
android:id="@id/messageText"