Merge pull request #3155 from nextcloud/feature/3154/chatTextFormatting

Implement text formatting via markdown
This commit is contained in:
Andy Scherzinger 2023-07-10 19:42:24 +02:00 committed by GitHub
commit f913ccb95f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 327 additions and 112 deletions

View File

@ -144,12 +144,13 @@ ext {
emojiVersion = "1.3.0" emojiVersion = "1.3.0"
lifecycleVersion = '2.6.1' lifecycleVersion = '2.6.1'
okhttpVersion = "4.11.0" okhttpVersion = "4.11.0"
markwonVersion = "4.6.2"
materialDialogsVersion = "3.3.0" materialDialogsVersion = "3.3.0"
parcelerVersion = "1.1.13" parcelerVersion = "1.1.13"
prismVersion = "2.0.0"
retrofit2Version = "2.9.0" retrofit2Version = "2.9.0"
roomVersion = "2.5.2" roomVersion = "2.5.2"
workVersion = "2.8.1" workVersion = "2.8.1"
markwonVersion = "4.6.2"
espressoVersion = "3.5.1" espressoVersion = "3.5.1"
} }
@ -157,6 +158,7 @@ configurations.all {
exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
exclude group: 'org.jetbrains', module: 'annotations-java5' // via prism4j, already using annotations explicitly
} }
dependencies { dependencies {
@ -268,6 +270,8 @@ dependencies {
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.27' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.27'
implementation "io.noties.markwon:core:$markwonVersion" implementation "io.noties.markwon:core:$markwonVersion"
implementation "io.noties.markwon:ext-strikethrough:$markwonVersion"
implementation "io.noties.markwon:ext-tasklist:$markwonVersion"
implementation 'com.github.nextcloud-deps:ImagePicker:2.1.0.2' implementation 'com.github.nextcloud-deps:ImagePicker:2.1.0.2'
implementation 'com.elyeproj.libraries:loaderviewlibrary:2.0.0' implementation 'com.elyeproj.libraries:loaderviewlibrary:2.0.0'

View File

@ -40,6 +40,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject import javax.inject.Inject
@ -60,6 +61,9 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -77,6 +81,22 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
sharedApplication!!.componentApplication.inject(this) sharedApplication!!.componentApplication.inject(this)
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp) binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
var processedMessageText = messageUtils.enrichChatMessageText(
binding.messageText.context,
message,
binding.messageText.context.resources.getColor(R.color.nc_incoming_text_default)
)
processedMessageText = messageUtils.processMessageParameters(
binding.messageText.context,
viewThemeUtils,
processedMessageText!!,
message,
itemView
)
binding.messageText.text = processedMessageText
setAvatarAndAuthorOnMessageItem(message) setAvatarAndAuthorOnMessageItem(message)
colorizeMessageBubble(message) colorizeMessageBubble(message)
@ -174,7 +194,14 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest) ?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
binding.messageQuote.quotedMessage.context.resources.getColor(
R.color.nc_incoming_text_default
)
)
binding.messageQuote.quotedMessageAuthor binding.messageQuote.quotedMessageAuthor
.setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast)) .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast))

View File

@ -51,6 +51,7 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.UriUtils import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import java.net.URLEncoder import java.net.URLEncoder
@ -76,6 +77,9 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -175,7 +179,14 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
binding.messageQuote.quotedMessage.context.resources.getColor(
R.color.nc_incoming_text_default
)
)
binding.messageQuote.quotedMessageAuthor binding.messageQuote.quotedMessageAuthor
.setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast, null)) .setTextColor(context!!.resources.getColor(R.color.textColorMaxContrast, null))

View File

@ -40,6 +40,7 @@ import com.nextcloud.talk.polls.ui.PollMainDialogFragment
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject import javax.inject.Inject
@ -59,6 +60,9 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -198,7 +202,14 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest) ?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
binding.messageQuote.quotedMessage.context.resources.getColor(
R.color.nc_incoming_text_default
)
)
binding.messageQuote.quotedMessageAuthor binding.messageQuote.quotedMessageAuthor
.setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast)) .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast))

View File

@ -27,10 +27,6 @@
package com.nextcloud.talk.adapters.messages package com.nextcloud.talk.adapters.messages
import android.content.Context import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.Spannable
import android.text.SpannableString
import android.text.TextUtils import android.text.TextUtils
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
@ -50,6 +46,7 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.TextMatchers import com.nextcloud.talk.utils.TextMatchers
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject import javax.inject.Inject
@ -66,6 +63,9 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var appPreferences: AppPreferences lateinit var appPreferences: AppPreferences
@ -84,21 +84,34 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
itemView.isSelected = false itemView.isSelected = false
var messageString: Spannable = SpannableString(message.message)
var textSize = context.resources!!.getDimension(R.dimen.chat_text_size) var textSize = context.resources!!.getDimension(R.dimen.chat_text_size)
var processedMessageText = messageUtils.enrichChatMessageText(
binding.messageText.context,
message,
binding.messageText.context.resources.getColor(R.color.nc_incoming_text_default)
)
processedMessageText = messageUtils.processMessageParameters(
binding.messageText.context,
viewThemeUtils,
processedMessageText!!,
message,
itemView
)
val messageParameters = message.messageParameters val messageParameters = message.messageParameters
if (messageParameters != null && messageParameters.size > 0) { if (
messageString = processMessageParameters(messageParameters, message, messageString) (messageParameters == null || messageParameters.size <= 0) &&
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) { TextMatchers.isMessageWithSingleEmoticonOnly(message.text)
) {
textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat() textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat()
itemView.isSelected = true itemView.isSelected = true
binding.messageAuthor.visibility = View.GONE binding.messageAuthor.visibility = View.GONE
} }
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageText.text = messageString binding.messageText.text = processedMessageText
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp) binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
@ -189,7 +202,12 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
} else { } else {
parentChatMessage.actorDisplayName parentChatMessage.actorDisplayName
} }
binding.messageQuote.quotedMessage.text = DisplayUtils.ellipsize(parentChatMessage.text, MAX_REPLY_LENGTH) binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
DisplayUtils.ellipsize(parentChatMessage.text, MAX_REPLY_LENGTH),
binding.messageQuote.quotedMessage.context.resources.getColor(R.color.nc_incoming_text_default)
)
if (parentChatMessage.actorId?.equals(message.activeUser!!.userId) == true) { if (parentChatMessage.actorId?.equals(message.activeUser!!.userId) == true) {
viewThemeUtils.platform.colorViewBackground(binding.messageQuote.quoteColoredView) viewThemeUtils.platform.colorViewBackground(binding.messageQuote.quoteColoredView)
@ -216,46 +234,6 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
} }
} }
private fun processMessageParameters(
messageParameters: HashMap<String?, HashMap<String?, String?>>,
message: ChatMessage,
messageString: Spannable
): Spannable {
var messageStringInternal = messageString
for (key in messageParameters.keys) {
val individualHashMap = message.messageParameters!![key]
if (individualHashMap != null) {
when (individualHashMap["type"]) {
"user", "guest", "call", "user-group" -> {
val chip = if (individualHashMap["id"] == message.activeUser!!.userId) {
R.xml.chip_you
} else {
R.xml.chip_others
}
messageStringInternal = DisplayUtils.searchAndReplaceWithMentionSpan(
key,
binding.messageText.context,
messageStringInternal,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
chip,
viewThemeUtils
)
}
"file" -> {
itemView.setOnClickListener { v ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
context.startActivity(browserIntent)
}
}
}
}
}
return messageStringInternal
}
fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) { fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
this.commonMessageInterface = commonMessageInterface this.commonMessageInterface = commonMessageInterface
} }

View File

@ -49,6 +49,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
@ -67,6 +68,9 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -285,7 +289,14 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
binding.messageQuote.quotedMessage.context.resources.getColor(
R.color.nc_incoming_text_default
)
)
binding.messageQuote.quotedMessageAuthor binding.messageQuote.quotedMessageAuthor
.setTextColor(ContextCompat.getColor(context!!, R.color.textColorMaxContrast)) .setTextColor(ContextCompat.getColor(context!!, R.color.textColorMaxContrast))

View File

@ -39,6 +39,7 @@ import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject import javax.inject.Inject
@ -56,6 +57,9 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -79,6 +83,16 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp) binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
colorizeMessageBubble(message) colorizeMessageBubble(message)
var processedMessageText = messageUtils.enrichChatMessageText(binding.messageText.context, message, textColor)
processedMessageText = messageUtils.processMessageParameters(
binding.messageText.context,
viewThemeUtils,
processedMessageText!!,
message,
itemView
)
binding.messageText.text = processedMessageText
itemView.isSelected = false itemView.isSelected = false
@ -158,7 +172,12 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest) ?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
viewThemeUtils.getScheme(binding.messageQuote.quotedMessage.context).onSurfaceVariant
)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

View File

@ -48,6 +48,7 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.UriUtils import com.nextcloud.talk.utils.UriUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import java.net.URLEncoder import java.net.URLEncoder
import javax.inject.Inject import javax.inject.Inject
@ -71,6 +72,9 @@ class OutcomingLocationMessageViewHolder(incomingView: View) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -221,7 +225,12 @@ class OutcomingLocationMessageViewHolder(incomingView: View) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
viewThemeUtils.getScheme(binding.messageQuote.quotedMessage.context).onSurfaceVariant
)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

View File

@ -40,6 +40,7 @@ import com.nextcloud.talk.polls.ui.PollMainDialogFragment
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import javax.inject.Inject import javax.inject.Inject
@ -56,6 +57,9 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -183,7 +187,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context.getText(R.string.nc_nick_guest) ?: context.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
viewThemeUtils.getScheme(binding.messageQuote.quotedMessage.context).onSurfaceVariant
)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

View File

@ -24,11 +24,7 @@
package com.nextcloud.talk.adapters.messages package com.nextcloud.talk.adapters.messages
import android.content.Context import android.content.Context
import android.content.Intent
import android.graphics.PorterDuff import android.graphics.PorterDuff
import android.net.Uri
import android.text.Spannable
import android.text.SpannableString
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@ -47,6 +43,7 @@ import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.TextMatchers import com.nextcloud.talk.utils.TextMatchers
import com.nextcloud.talk.utils.message.MessageUtils
import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
import javax.inject.Inject import javax.inject.Inject
@ -61,6 +58,9 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -69,17 +69,27 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
override fun onBind(message: ChatMessage) { override fun onBind(message: ChatMessage) {
super.onBind(message) super.onBind(message)
sharedApplication!!.componentApplication.inject(this) sharedApplication!!.componentApplication.inject(this)
val messageParameters: HashMap<String?, HashMap<String?, String?>>? = message.messageParameters
var messageString: Spannable = SpannableString(message.message)
realView.isSelected = false realView.isSelected = false
val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
layoutParams.isWrapBefore = false layoutParams.isWrapBefore = false
var textSize = context!!.resources.getDimension(R.dimen.chat_text_size) var textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
val textColor = viewThemeUtils.getScheme(binding.messageText.context).onSurfaceVariant val textColor = viewThemeUtils.getScheme(binding.messageText.context).onSurfaceVariant
binding.messageTime.setTextColor(textColor) binding.messageTime.setTextColor(textColor)
if (messageParameters != null && messageParameters.size > 0) {
messageString = processMessageParameters(messageParameters, message, messageString) var processedMessageText = messageUtils.enrichChatMessageText(binding.messageText.context, message, textColor)
} else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) { processedMessageText = messageUtils.processMessageParameters(
binding.messageText.context,
viewThemeUtils,
processedMessageText!!,
message,
itemView
)
val messageParameters = message.messageParameters
if (
(messageParameters == null || messageParameters.size <= 0) &&
TextMatchers.isMessageWithSingleEmoticonOnly(message.text)
) {
textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat() textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat()
layoutParams.isWrapBefore = true layoutParams.isWrapBefore = true
realView.isSelected = true realView.isSelected = true
@ -90,7 +100,7 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
binding.messageTime.layoutParams = layoutParams binding.messageTime.layoutParams = layoutParams
binding.messageText.setTextColor(textColor) binding.messageText.setTextColor(textColor)
binding.messageText.text = messageString binding.messageText.text = processedMessageText
binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp) binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
@ -164,7 +174,12 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = DisplayUtils.ellipsize(parentChatMessage.text, MAX_REPLY_LENGTH) binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
DisplayUtils.ellipsize(parentChatMessage.text, MAX_REPLY_LENGTH),
textColor
)
binding.messageQuote.quotedMessageAuthor.setTextColor(textColor) binding.messageQuote.quotedMessageAuthor.setTextColor(textColor)
binding.messageQuote.quotedMessage.setTextColor(textColor) binding.messageQuote.quotedMessage.setTextColor(textColor)
@ -180,46 +195,6 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted) viewThemeUtils.talk.themeOutgoingMessageBubble(bubble, message.isGrouped, message.isDeleted)
} }
private fun processMessageParameters(
messageParameters: HashMap<String?, HashMap<String?, String?>>,
message: ChatMessage,
messageString: Spannable
): Spannable {
var messageStringInternal = messageString
for (key in messageParameters.keys) {
val individualHashMap: HashMap<String?, String?>? = message.messageParameters!![key]
if (individualHashMap != null) {
when (individualHashMap["type"]) {
"user", "guest", "call", "user-group" -> {
val chip = if (individualHashMap["id"] == message.activeUser!!.userId) {
R.xml.chip_you
} else {
R.xml.chip_others
}
messageStringInternal = DisplayUtils.searchAndReplaceWithMentionSpan(
key,
binding.messageText.context,
messageStringInternal,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
chip,
viewThemeUtils
)
}
"file" -> {
itemView.setOnClickListener { v ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
context.startActivity(browserIntent)
}
}
}
}
}
return messageStringInternal
}
fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) { fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) {
this.commonMessageInterface = commonMessageInterface this.commonMessageInterface = commonMessageInterface
} }

View File

@ -45,6 +45,7 @@ import com.nextcloud.talk.models.json.chat.ReadStatus
import com.nextcloud.talk.ui.theme.ViewThemeUtils import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.stfalcon.chatkit.messages.MessageHolders import com.stfalcon.chatkit.messages.MessageHolders
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
@ -63,6 +64,9 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) :
@Inject @Inject
lateinit var viewThemeUtils: ViewThemeUtils lateinit var viewThemeUtils: ViewThemeUtils
@Inject
lateinit var messageUtils: MessageUtils
@Inject @Inject
lateinit var dateUtils: DateUtils lateinit var dateUtils: DateUtils
@ -271,7 +275,12 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) :
} }
binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
?: context!!.getText(R.string.nc_nick_guest) ?: context!!.getText(R.string.nc_nick_guest)
binding.messageQuote.quotedMessage.text = parentChatMessage.text binding.messageQuote.quotedMessage.text = messageUtils
.enrichChatMessageText(
binding.messageQuote.quotedMessage.context,
parentChatMessage.text,
viewThemeUtils.getScheme(binding.messageQuote.quotedMessage.context).onSurfaceVariant
)
viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage)
viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

View File

@ -23,6 +23,7 @@ package com.nextcloud.talk.dagger.modules
import android.content.Context import android.content.Context
import com.nextcloud.talk.utils.DateUtils import com.nextcloud.talk.utils.DateUtils
import com.nextcloud.talk.utils.message.MessageUtils
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtilImpl import com.nextcloud.talk.utils.permissions.PlatformPermissionUtilImpl
import dagger.Module import dagger.Module
@ -42,4 +43,10 @@ class UtilsModule {
fun provideDateUtils(context: Context): DateUtils { fun provideDateUtils(context: Context): DateUtils {
return DateUtils(context) return DateUtils(context)
} }
@Provides
@Reusable
fun provideMessageUtils(context: Context): MessageUtils {
return MessageUtils(context)
}
} }

View File

@ -97,6 +97,7 @@ import static com.nextcloud.talk.utils.FileSortOrder.sort_small_to_big_id;
import static com.nextcloud.talk.utils.FileSortOrder.sort_z_to_a_id; import static com.nextcloud.talk.utils.FileSortOrder.sort_z_to_a_id;
public class DisplayUtils { public class DisplayUtils {
private static final String TAG = DisplayUtils.class.getSimpleName();
private static final int INDEX_LUMINATION = 2; private static final int INDEX_LUMINATION = 2;
private static final double MAX_LIGHTNESS = 0.92; private static final double MAX_LIGHTNESS = 0.92;
@ -246,7 +247,7 @@ public class DisplayUtils {
return chip; return chip;
} }
public static Spannable searchAndReplaceWithMentionSpan(String key, Context context, Spannable text, public static Spannable searchAndReplaceWithMentionSpan(String key, Context context, Spanned text,
String id, String label, String type, String id, String label, String type,
User conversationUser, User conversationUser,
@XmlRes int chipXmlRes, @XmlRes int chipXmlRes,

View File

@ -0,0 +1,143 @@
/*
* Nextcloud Talk application
*
* @author Andy Scherzinger
* Copyright (C) 2023 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.utils.message
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.Spanned
import android.util.Log
import android.view.View
import com.nextcloud.talk.R
import com.nextcloud.talk.models.json.chat.ChatMessage
import com.nextcloud.talk.ui.theme.ViewThemeUtils
import com.nextcloud.talk.utils.DisplayUtils
import io.noties.markwon.AbstractMarkwonPlugin
import io.noties.markwon.Markwon
import io.noties.markwon.MarkwonConfiguration
import io.noties.markwon.core.MarkwonTheme
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
import io.noties.markwon.ext.tasklist.TaskListDrawable
import io.noties.markwon.ext.tasklist.TaskListPlugin
class MessageUtils(val context: Context) {
fun enrichChatMessageText(context: Context, message: ChatMessage, textColor: Int): Spanned? {
return if (message.message == null) {
null
} else {
enrichChatMessageText(context, message.message!!, textColor)
}
}
fun enrichChatMessageText(context: Context, message: String, textColor: Int): Spanned {
return getRenderedMarkdownText(context, message, textColor)
}
fun processMessageParameters(
themingContext: Context,
viewThemeUtils: ViewThemeUtils,
spannedText: Spanned,
message: ChatMessage,
itemView: View
): Spanned {
var processedMessageText = spannedText
val messageParameters = message.messageParameters
if (messageParameters != null && messageParameters.size > 0) {
processedMessageText = processMessageParameters(
themingContext,
viewThemeUtils,
messageParameters,
message,
processedMessageText,
itemView
)
}
return processedMessageText
}
private fun processMessageParameters(
themingContext: Context,
viewThemeUtils: ViewThemeUtils,
messageParameters: HashMap<String?, HashMap<String?, String?>>,
message: ChatMessage,
messageString: Spanned,
itemView: View
): Spanned {
var messageStringInternal = messageString
for (key in messageParameters.keys) {
val individualHashMap = message.messageParameters!![key]
if (individualHashMap != null) {
when (individualHashMap["type"]) {
"user", "guest", "call", "user-group" -> {
val chip = if (individualHashMap["id"] == message.activeUser!!.userId) {
R.xml.chip_you
} else {
R.xml.chip_others
}
messageStringInternal = DisplayUtils.searchAndReplaceWithMentionSpan(
key,
themingContext,
messageStringInternal,
individualHashMap["id"]!!,
individualHashMap["name"]!!,
individualHashMap["type"]!!,
message.activeUser!!,
chip,
viewThemeUtils
)
}
"file" -> {
itemView.setOnClickListener { v ->
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(individualHashMap["link"]))
context.startActivity(browserIntent)
}
}
}
}
}
return messageStringInternal
}
private fun getRenderedMarkdownText(context: Context, markdown: String, textColor: Int): Spanned {
val drawable = TaskListDrawable(textColor, textColor, context.getColor(R.color.bg_default))
val markwon = Markwon.builder(context).usePlugin(object : AbstractMarkwonPlugin() {
override fun configureTheme(builder: MarkwonTheme.Builder) {
builder.isLinkUnderlined(true).headingBreakHeight(0)
}
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.linkResolver { view: View?, link: String? ->
Log.i(TAG, "Link action not implemented $view / $link")
}
}
})
.usePlugin(TaskListPlugin.create(drawable))
.usePlugin(StrikethroughPlugin.create()).build()
return markwon.toMarkdown(markdown)
}
companion object {
private const val TAG = "MessageUtils"
}
}

View File

@ -47,6 +47,7 @@ buildscript {
} }
configurations.all { configurations.all {
exclude group: 'org.jetbrains', module: 'annotations-java5' // via prism4j, already using annotations explicitly
// check for updates every build // check for updates every build
resolutionStrategy.cacheChangingModulesFor 3600, 'seconds' resolutionStrategy.cacheChangingModulesFor 3600, 'seconds'
} }