From e954fd860a946950ca39c96857b62e83db799492 Mon Sep 17 00:00:00 2001 From: Thomas Date: Mon, 7 Aug 2023 17:03:14 +0200 Subject: [PATCH] Markdown support --- app/build.gradle | 5 + .../app/fedilab/android/MainApplication.java | 2 + .../entities/app/MarkdownConverter.java | 10 ++ .../mastodon/helper/SpannableHelper.java | 99 +++++++++++++------ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/pref_timelines.xml | 7 ++ 6 files changed, 93 insertions(+), 32 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index beb7c261..0322bcae 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -85,6 +85,8 @@ android { } } configurations { + cleanedAnnotations + implementation.exclude group: 'org.jetbrains', module: 'annotations' all { exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx' } @@ -186,6 +188,9 @@ dependencies { implementation 'io.noties.markwon:core:4.6.2' + implementation 'io.noties.markwon:ext-tables:4.6.2' + implementation 'io.noties.markwon:syntax-highlight:4.6.2' + annotationProcessor 'io.noties:prism4j-bundler:2.0.0' //************ CAST **************/// diff --git a/app/src/main/java/app/fedilab/android/MainApplication.java b/app/src/main/java/app/fedilab/android/MainApplication.java index 8ad75b08..4192a96d 100644 --- a/app/src/main/java/app/fedilab/android/MainApplication.java +++ b/app/src/main/java/app/fedilab/android/MainApplication.java @@ -42,7 +42,9 @@ import java.util.Objects; import app.fedilab.android.mastodon.helper.ThemeHelper; import app.fedilab.android.peertube.services.GlobalUploadObserver; import es.dmoral.toasty.Toasty; +import io.noties.prism4j.annotations.PrismBundle; +@PrismBundle(includeAll = true, grammarLocatorClassName = ".MySuperGrammerLocator") public class MainApplication extends MultiDexApplication { diff --git a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/MarkdownConverter.java b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/MarkdownConverter.java index 54dd38d5..cc074fb6 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/MarkdownConverter.java +++ b/app/src/main/java/app/fedilab/android/mastodon/client/entities/app/MarkdownConverter.java @@ -37,5 +37,15 @@ public class MarkdownConverter { public String code; public int position; public URLSpan urlSpan; + + public int regexPosition(List markdownItems) { + int position = 0; + for (MarkdownItem markdownItem : markdownItems) { + if (markdownItem.code.equals(code) && position <= this.position) { + position++; + } + } + return position; + } } } diff --git a/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java b/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java index 50c4d847..5341c377 100644 --- a/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java +++ b/app/src/main/java/app/fedilab/android/mastodon/helper/SpannableHelper.java @@ -43,6 +43,7 @@ import android.view.View; import android.webkit.URLUtil; import android.widget.Toast; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; @@ -71,6 +72,7 @@ import java.util.regex.Pattern; import javax.net.ssl.HttpsURLConnection; import app.fedilab.android.BaseMainActivity; +import app.fedilab.android.MySuperGrammerLocator; import app.fedilab.android.R; import app.fedilab.android.activities.MainActivity; import app.fedilab.android.databinding.PopupLinksBinding; @@ -89,6 +91,10 @@ import app.fedilab.android.mastodon.ui.drawer.StatusAdapter; import app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM; import es.dmoral.toasty.Toasty; import io.noties.markwon.Markwon; +import io.noties.markwon.ext.tables.TablePlugin; +import io.noties.markwon.syntax.Prism4jThemeDefault; +import io.noties.markwon.syntax.SyntaxHighlightPlugin; +import io.noties.prism4j.Prism4j; public class SpannableHelper { @@ -149,44 +155,56 @@ public class SpannableHelper { } else { initialContent = new SpannableString(text); } - + boolean markdownSupport = sharedpreferences.getBoolean(context.getString(R.string.SET_MARKDOWN_SUPPORT), true); //Get all links - MarkdownConverter markdownConverter = new MarkdownConverter(); - markdownConverter.markdownItems = new ArrayList<>(); - int next; - int position = 0; - for (int i = 0; i < initialContent.length(); i = next) { - // find the next span transition - next = initialContent.nextSpanTransition(i, initialContent.length(), URLSpan.class); - MarkdownConverter.MarkdownItem markdownItem = new MarkdownConverter.MarkdownItem(); - markdownItem.code = initialContent.subSequence(i, next).toString(); - - markdownItem.position = position; - // get all spans in this range - URLSpan[] spans = initialContent.getSpans(i, next, URLSpan.class); - if (spans != null && spans.length > 0) { - markdownItem.urlSpan = spans[0]; + SpannableStringBuilder content; + if (markdownSupport) { + MarkdownConverter markdownConverter = new MarkdownConverter(); + markdownConverter.markdownItems = new ArrayList<>(); + int next; + int position = 0; + for (int i = 0; i < initialContent.length(); i = next) { + // find the next span transition + next = initialContent.nextSpanTransition(i, initialContent.length(), URLSpan.class); + MarkdownConverter.MarkdownItem markdownItem = new MarkdownConverter.MarkdownItem(); + markdownItem.code = initialContent.subSequence(i, next).toString(); + + markdownItem.position = position; + // get all spans in this range + URLSpan[] spans = initialContent.getSpans(i, next, URLSpan.class); + if (spans != null && spans.length > 0) { + markdownItem.urlSpan = spans[0]; + } + + if (markdownItem.code.trim().length() > 0) { + markdownConverter.markdownItems.add(markdownItem); + position++; + } } - if (markdownItem.code.trim().length() > 0) { - markdownConverter.markdownItems.add(markdownItem); + final Markwon markwon = Markwon.builder(context) + .usePlugin(TablePlugin.create(context)) + .usePlugin(SyntaxHighlightPlugin.create(new Prism4j(new MySuperGrammerLocator()), Prism4jThemeDefault.create())).build(); + + final Spanned markdown = markwon.toMarkdown(initialContent.toString()); + content = new SpannableStringBuilder(markdown); + position = 0; + for (MarkdownConverter.MarkdownItem markdownItem : markdownConverter.markdownItems) { + Pattern p = Pattern.compile("(" + Pattern.quote(markdownItem.code) + ")", Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(content); + int fetchPosition = 1; + while (m.find()) { + int regexPosition = markdownItem.regexPosition(markdownConverter.markdownItems); + if (regexPosition == fetchPosition) { + content.setSpan(markdownItem.urlSpan, m.start(), m.end(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + fetchPosition++; + } position++; } + } else { + content = new SpannableStringBuilder(initialContent); } - final Markwon markwon = Markwon.create(context); - - final Spanned markdown = markwon.toMarkdown(initialContent.toString()); - SpannableStringBuilder content = new SpannableStringBuilder(markdown); - position = 0; - for (MarkdownConverter.MarkdownItem markdownItem : markdownConverter.markdownItems) { - Pattern p = Pattern.compile("(" + Pattern.quote(markdownItem.code) + ")", Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(content); - while (m.find()) { - content.setSpan(markdownItem.urlSpan, m.start(), m.end(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - position++; - } - URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class); //Loop through links @@ -336,6 +354,23 @@ public class SpannableHelper { return trimSpannable(new SpannableStringBuilder(content)); } + public interface Prism4jTheme { + + @ColorInt + int background(); + + @ColorInt + int textColor(); + + void apply( + @NonNull String language, + @NonNull Prism4j.Syntax syntax, + @NonNull SpannableStringBuilder builder, + int start, + int end + ); + } + private static void makeLinks(Context context, SpannableStringBuilder content, String url, int start, int end) { String newUrl = url; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5697d851..45dee809 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1156,6 +1156,7 @@ SET_GROUP_REBLOGS SET_BOOST_ORIGINAL_DATE + SET_MARKDOWN_SUPPORT SET_TRUNCATE_LINKS SET_TRUNCATE_LINKS_MAX @@ -1907,6 +1908,7 @@ Followed by: Directory Display original date for boosts + Markdown support Disable release notes When a new version is published, you will not be alerted inside the app. Formula diff --git a/app/src/main/res/xml/pref_timelines.xml b/app/src/main/res/xml/pref_timelines.xml index 410c18e6..6c5fc27b 100644 --- a/app/src/main/res/xml/pref_timelines.xml +++ b/app/src/main/res/xml/pref_timelines.xml @@ -50,6 +50,13 @@ app:singleLineTitle="false" app:title="@string/boost_original_date" /> + +