Don't linkify inside escaped/codeblocks

Stephen Paul Weber created

Change summary

src/main/java/eu/siacs/conversations/ui/util/MyLinkify.java   | 14 ++++
src/main/java/eu/siacs/conversations/utils/StylingHelper.java | 10 +++
2 files changed, 22 insertions(+), 2 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/util/MyLinkify.java 🔗

@@ -32,6 +32,8 @@ package eu.siacs.conversations.ui.util;
 import android.net.Uri;
 import android.os.Build;
 import android.text.Editable;
+import android.text.Spanned;
+import android.text.style.TypefaceSpan;
 import android.text.style.URLSpan;
 import android.text.util.Linkify;
 
@@ -52,6 +54,7 @@ import eu.siacs.conversations.entities.Roster;
 import eu.siacs.conversations.ui.text.FixedURLSpan;
 import eu.siacs.conversations.utils.GeoHelper;
 import eu.siacs.conversations.utils.Patterns;
+import eu.siacs.conversations.utils.StylingHelper;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
 
@@ -139,7 +142,16 @@ public class MyLinkify {
     public static void addLinks(Editable body, Account account, Jid context) {
         addLinks(body, true);
         Roster roster = account.getRoster();
+        urlspan:
         for (final URLSpan urlspan : body.getSpans(0, body.length() - 1, URLSpan.class)) {
+            final var start = body.getSpanStart(urlspan);
+            for (final var span : body.getSpans(start, start, Object.class))  {
+                // instanceof TypefaceSpan is to block in XHTML code blocks. Probably a bit heavy-handed but works for now
+                if ((body.getSpanFlags(span) & Spanned.SPAN_USER) >> Spanned.SPAN_USER_SHIFT == StylingHelper.NOLINKIFY || span instanceof TypefaceSpan) {
+                    body.removeSpan(urlspan);
+                    continue urlspan;
+                }
+            }
             Uri uri = Uri.parse(urlspan.getURL());
             if ("xmpp".equals(uri.getScheme())) {
                 try {
@@ -153,7 +165,7 @@ public class MyLinkify {
                     String display = xmppUri.toString();
                     if (jid.asBareJid().equals(context) && xmppUri.isAction("message") && xmppUri.getBody() != null) {
                         display = xmppUri.getBody();
-                    } else if (jid.asBareJid().equals(context)) {
+                    } else if (jid.asBareJid().equals(context) && xmppUri.parameterString().length() > 0) {
                         display = xmppUri.parameterString();
                     } else {
                         ListItem item = account.getBookmark(jid);

src/main/java/eu/siacs/conversations/utils/StylingHelper.java 🔗

@@ -67,6 +67,7 @@ public class StylingHelper {
 	public static final int XHTML_IGNORE = 1;
 	public static final int XHTML_REMOVE = 2;
 	public static final int XHTML_EMPHASIS = 3;
+	public static final int NOLINKIFY = 0xf0;
 
 	private static final List<? extends Class<? extends ParcelableSpan>> SPAN_CLASSES = Arrays.asList(
 			StyleSpan.class,
@@ -88,7 +89,14 @@ public class StylingHelper {
 	public static void format(final Editable editable, int start, int end, @ColorInt int textColor, final boolean composing) {
 		for (ImStyleParser.Style style : ImStyleParser.parse(editable, start, end)) {
 			final int keywordLength = style.getKeyword().length();
-			editable.setSpan(createSpanForStyle(style), style.getStart() + keywordLength, style.getEnd() - keywordLength + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | ("*".equals(style.getKeyword()) || "_".equals(style.getKeyword()) ? XHTML_EMPHASIS << Spanned.SPAN_USER_SHIFT : 0));
+			editable.setSpan(
+				createSpanForStyle(style),
+				style.getStart() + keywordLength,
+				style.getEnd() - keywordLength + 1,
+				Spanned.SPAN_EXCLUSIVE_EXCLUSIVE |
+					("*".equals(style.getKeyword()) || "_".equals(style.getKeyword()) ? XHTML_EMPHASIS << Spanned.SPAN_USER_SHIFT : 0) |
+					("`".equals(style.getKeyword()) || "```".equals(style.getKeyword()) ? NOLINKIFY << Spanned.SPAN_USER_SHIFT : 0)
+			);
 			makeKeywordOpaque(editable, style.getStart(), style.getStart() + keywordLength + ("```".equals(style.getKeyword()) ? 1 : 0), textColor, composing);
 			makeKeywordOpaque(editable, style.getEnd() - keywordLength + 1, style.getEnd() + 1, textColor, composing);
 		}