Przeglądaj źródła

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

Implement text formatting via markdown
Andy Scherzinger 2 lat temu
rodzic
commit
f913ccb95f

+ 5 - 1
app/build.gradle

@@ -144,12 +144,13 @@ ext {
     emojiVersion = "1.3.0"
     lifecycleVersion = '2.6.1'
     okhttpVersion = "4.11.0"
+    markwonVersion = "4.6.2"
     materialDialogsVersion = "3.3.0"
     parcelerVersion = "1.1.13"
+    prismVersion = "2.0.0"
     retrofit2Version = "2.9.0"
     roomVersion = "2.5.2"
     workVersion = "2.8.1"
-    markwonVersion = "4.6.2"
     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-analytics'
     exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
+    exclude group: 'org.jetbrains', module: 'annotations-java5' // via prism4j, already using annotations explicitly
 }
 
 dependencies {
@@ -268,6 +270,8 @@ dependencies {
     implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.27'
 
     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.elyeproj.libraries:loaderviewlibrary:2.0.0'

+ 28 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt

@@ -40,6 +40,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
 import com.stfalcon.chatkit.messages.MessageHolders
 import javax.inject.Inject
@@ -60,6 +61,9 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var dateUtils: DateUtils
 
@@ -77,6 +81,22 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
         sharedApplication!!.componentApplication.inject(this)
         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)
 
         colorizeMessageBubble(message)
@@ -174,7 +194,14 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) :
             }
             binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
                 ?: 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
                 .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast))

+ 12 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt

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

+ 12 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt

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

+ 29 - 51
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt

@@ -27,10 +27,6 @@
 package com.nextcloud.talk.adapters.messages
 
 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.util.TypedValue
 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.DisplayUtils
 import com.nextcloud.talk.utils.TextMatchers
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
 import com.stfalcon.chatkit.messages.MessageHolders
 import javax.inject.Inject
@@ -66,6 +63,9 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var appPreferences: AppPreferences
 
@@ -84,21 +84,34 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
 
         itemView.isSelected = false
 
-        var messageString: Spannable = SpannableString(message.message)
-
         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
-        if (messageParameters != null && messageParameters.size > 0) {
-            messageString = processMessageParameters(messageParameters, message, messageString)
-        } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
+        if (
+            (messageParameters == null || messageParameters.size <= 0) &&
+            TextMatchers.isMessageWithSingleEmoticonOnly(message.text)
+        ) {
             textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat()
             itemView.isSelected = true
             binding.messageAuthor.visibility = View.GONE
         }
 
         binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
-        binding.messageText.text = messageString
+        binding.messageText.text = processedMessageText
 
         binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
 
@@ -189,7 +202,12 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
         } else {
             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) {
             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) {
         this.commonMessageInterface = commonMessageInterface
     }

+ 12 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt

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

+ 20 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt

@@ -39,6 +39,7 @@ import com.nextcloud.talk.models.json.chat.ReadStatus
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
 import com.stfalcon.chatkit.messages.MessageHolders
 import javax.inject.Inject
@@ -56,6 +57,9 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var dateUtils: DateUtils
 
@@ -79,6 +83,16 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
         binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
 
         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
 
@@ -158,7 +172,12 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) :
             }
             binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
                 ?: 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.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
             viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

+ 10 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt

@@ -48,6 +48,7 @@ import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
 import com.nextcloud.talk.utils.UriUtils
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.stfalcon.chatkit.messages.MessageHolders
 import java.net.URLEncoder
 import javax.inject.Inject
@@ -71,6 +72,9 @@ class OutcomingLocationMessageViewHolder(incomingView: View) :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var dateUtils: DateUtils
 
@@ -221,7 +225,12 @@ class OutcomingLocationMessageViewHolder(incomingView: View) :
             }
             binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
                 ?: 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.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
             viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

+ 10 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt

@@ -40,6 +40,7 @@ import com.nextcloud.talk.polls.ui.PollMainDialogFragment
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
 import com.stfalcon.chatkit.messages.MessageHolders
 import javax.inject.Inject
@@ -56,6 +57,9 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var dateUtils: DateUtils
 
@@ -183,7 +187,12 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) :
             }
             binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
                 ?: 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.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
             viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

+ 26 - 51
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt

@@ -24,11 +24,7 @@
 package com.nextcloud.talk.adapters.messages
 
 import android.content.Context
-import android.content.Intent
 import android.graphics.PorterDuff
-import android.net.Uri
-import android.text.Spannable
-import android.text.SpannableString
 import android.util.TypedValue
 import android.view.View
 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.DisplayUtils
 import com.nextcloud.talk.utils.TextMatchers
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.stfalcon.chatkit.messages.MessageHolders.OutcomingTextMessageViewHolder
 import javax.inject.Inject
 
@@ -61,6 +58,9 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var dateUtils: DateUtils
 
@@ -69,17 +69,27 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
     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.message)
         realView.isSelected = false
         val layoutParams = binding.messageTime.layoutParams as FlexboxLayout.LayoutParams
         layoutParams.isWrapBefore = false
         var textSize = context!!.resources.getDimension(R.dimen.chat_text_size)
         val textColor = viewThemeUtils.getScheme(binding.messageText.context).onSurfaceVariant
         binding.messageTime.setTextColor(textColor)
-        if (messageParameters != null && messageParameters.size > 0) {
-            messageString = processMessageParameters(messageParameters, message, messageString)
-        } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
+
+        var processedMessageText = messageUtils.enrichChatMessageText(binding.messageText.context, message, textColor)
+        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()
             layoutParams.isWrapBefore = true
             realView.isSelected = true
@@ -90,7 +100,7 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
         binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
         binding.messageTime.layoutParams = layoutParams
         binding.messageText.setTextColor(textColor)
-        binding.messageText.text = messageString
+        binding.messageText.text = processedMessageText
 
         binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
 
@@ -164,7 +174,12 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
         }
         binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
             ?: 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.quotedMessage.setTextColor(textColor)
@@ -180,46 +195,6 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
         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) {
         this.commonMessageInterface = commonMessageInterface
     }

+ 10 - 1
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt

@@ -45,6 +45,7 @@ import com.nextcloud.talk.models.json.chat.ReadStatus
 import com.nextcloud.talk.ui.theme.ViewThemeUtils
 import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.DateUtils
+import com.nextcloud.talk.utils.message.MessageUtils
 import com.nextcloud.talk.utils.preferences.AppPreferences
 import com.stfalcon.chatkit.messages.MessageHolders
 import java.util.concurrent.ExecutionException
@@ -63,6 +64,9 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) :
     @Inject
     lateinit var viewThemeUtils: ViewThemeUtils
 
+    @Inject
+    lateinit var messageUtils: MessageUtils
+
     @Inject
     lateinit var dateUtils: DateUtils
 
@@ -271,7 +275,12 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) :
             }
             binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName
                 ?: 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.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor)
             viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView)

+ 7 - 0
app/src/main/java/com/nextcloud/talk/dagger/modules/UtilsModule.kt

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

+ 2 - 1
app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java

@@ -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;
 
 public class DisplayUtils {
+    private static final String TAG = DisplayUtils.class.getSimpleName();
 
     private static final int INDEX_LUMINATION = 2;
     private static final double MAX_LIGHTNESS = 0.92;
@@ -246,7 +247,7 @@ public class DisplayUtils {
         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,
                                                             User conversationUser,
                                                             @XmlRes int chipXmlRes,

+ 143 - 0
app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt

@@ -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"
+    }
+}

+ 1 - 0
build.gradle

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