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"
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'

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.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))

View File

@ -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))

View File

@ -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))

View File

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

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.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))

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.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)

View File

@ -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)

View File

@ -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)

View File

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

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.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)

View File

@ -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)
}
}

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;
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,

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 {
exclude group: 'org.jetbrains', module: 'annotations-java5' // via prism4j, already using annotations explicitly
// check for updates every build
resolutionStrategy.cacheChangingModulesFor 3600, 'seconds'
}