Эх сурвалжийг харах

Implement text formatting via markdown rendering for text messages

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Andy Scherzinger 2 жил өмнө
parent
commit
d124301809

+ 6 - 2
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 {
@@ -262,12 +264,14 @@ dependencies {
     implementation "com.afollestad.material-dialogs:lifecycle:${materialDialogsVersion}"
 
     implementation 'com.google.code.gson:gson:2.10.1'
-    implementation 'com.google.android.exoplayer:exoplayer:2.19.0'
+    implementation 'com.google.android.exoplayer:exoplayer:2.18.7'
 
     implementation 'com.github.chrisbanes:PhotoView:2.3.0'
     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:syntax-highlight:$markwonVersion"
 
     implementation 'com.github.nextcloud-deps:ImagePicker:2.1.0.2'
     implementation 'com.elyeproj.libraries:loaderviewlibrary:2.0.0'

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

@@ -29,8 +29,7 @@ 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.Spanned
 import android.text.TextUtils
 import android.util.TypedValue
 import android.view.View
@@ -84,13 +83,13 @@ 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 = DisplayUtils.getRenderedMarkdownText(context, message.message)
+
         val messageParameters = message.messageParameters
         if (messageParameters != null && messageParameters.size > 0) {
-            messageString = processMessageParameters(messageParameters, message, messageString)
+            processedMessageText = processMessageParameters(messageParameters, message, processedMessageText)
         } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
             textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat()
             itemView.isSelected = true
@@ -98,7 +97,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
         }
 
         binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
-        binding.messageText.text = messageString
+        binding.messageText.text = processedMessageText
 
         binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp)
 
@@ -219,8 +218,8 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
     private fun processMessageParameters(
         messageParameters: HashMap<String?, HashMap<String?, String?>>,
         message: ChatMessage,
-        messageString: Spannable
-    ): Spannable {
+        messageString: Spanned
+    ): Spanned {
         var messageStringInternal = messageString
         for (key in messageParameters.keys) {
             val individualHashMap = message.messageParameters!![key]
@@ -253,6 +252,7 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
                 }
             }
         }
+
         return messageStringInternal
     }
 

+ 8 - 7
app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt

@@ -27,8 +27,7 @@ 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.text.Spanned
 import android.util.TypedValue
 import android.view.View
 import androidx.core.content.res.ResourcesCompat
@@ -70,15 +69,17 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
         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
+
+        var processedMessageText = DisplayUtils.getRenderedMarkdownText(context, message.message)
+
         binding.messageTime.setTextColor(textColor)
         if (messageParameters != null && messageParameters.size > 0) {
-            messageString = processMessageParameters(messageParameters, message, messageString)
+            processedMessageText = processMessageParameters(messageParameters, message, processedMessageText)
         } else if (TextMatchers.isMessageWithSingleEmoticonOnly(message.text)) {
             textSize = (textSize * TEXT_SIZE_MULTIPLIER).toFloat()
             layoutParams.isWrapBefore = true
@@ -90,7 +91,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)
 
@@ -183,8 +184,8 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH
     private fun processMessageParameters(
         messageParameters: HashMap<String?, HashMap<String?, String?>>,
         message: ChatMessage,
-        messageString: Spannable
-    ): Spannable {
+        messageString: Spanned
+    ): Spanned {
         var messageStringInternal = messageString
         for (key in messageParameters.keys) {
             val individualHashMap: HashMap<String?, String?>? = message.messageParameters!![key]

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

@@ -48,6 +48,7 @@ import android.text.style.AbsoluteSizeSpan;
 import android.text.style.ClickableSpan;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.StyleSpan;
+import android.util.Log;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.Window;
@@ -87,6 +88,11 @@ import coil.Coil;
 import coil.request.ImageRequest;
 import coil.target.Target;
 import coil.transform.CircleCropTransformation;
+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 third.parties.fresco.BetterImageSpan;
 
 import static com.nextcloud.talk.utils.FileSortOrder.sort_a_to_z_id;
@@ -97,6 +103,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 +253,28 @@ public class DisplayUtils {
         return chip;
     }
 
-    public static Spannable searchAndReplaceWithMentionSpan(String key, Context context, Spannable text,
+    public static Spanned getRenderedMarkdownText(Context context, String markdown) {
+        final Markwon markwon = Markwon.builder(context)
+            .usePlugin(new AbstractMarkwonPlugin() {
+                @Override
+                public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
+                    builder.headingBreakHeight(0);
+                }
+
+                @Override
+                public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
+                    builder.linkResolver((view, link) -> {
+                        Log.i(TAG, "Link action not implemented" );
+                    });
+                }
+            })
+            .usePlugin(StrikethroughPlugin.create())
+            .build();
+
+        return markwon.toMarkdown(markdown);
+    }
+
+    public static Spannable searchAndReplaceWithMentionSpan(String key, Context context, Spanned text,
                                                             String id, String label, String type,
                                                             User conversationUser,
                                                             @XmlRes int chipXmlRes,

+ 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'
 }