mirror of
https://codeberg.org/tom79/Fedilab.git
synced 2024-12-22 16:50:04 +02:00
Markdown support
This commit is contained in:
parent
6790158ed9
commit
e954fd860a
6 changed files with 92 additions and 31 deletions
|
@ -85,6 +85,8 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
configurations {
|
configurations {
|
||||||
|
cleanedAnnotations
|
||||||
|
implementation.exclude group: 'org.jetbrains', module: 'annotations'
|
||||||
all {
|
all {
|
||||||
exclude group: 'androidx.lifecycle', module: 'lifecycle-viewmodel-ktx'
|
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: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 **************///
|
//************ CAST **************///
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@ import java.util.Objects;
|
||||||
import app.fedilab.android.mastodon.helper.ThemeHelper;
|
import app.fedilab.android.mastodon.helper.ThemeHelper;
|
||||||
import app.fedilab.android.peertube.services.GlobalUploadObserver;
|
import app.fedilab.android.peertube.services.GlobalUploadObserver;
|
||||||
import es.dmoral.toasty.Toasty;
|
import es.dmoral.toasty.Toasty;
|
||||||
|
import io.noties.prism4j.annotations.PrismBundle;
|
||||||
|
|
||||||
|
@PrismBundle(includeAll = true, grammarLocatorClassName = ".MySuperGrammerLocator")
|
||||||
|
|
||||||
public class MainApplication extends MultiDexApplication {
|
public class MainApplication extends MultiDexApplication {
|
||||||
|
|
||||||
|
|
|
@ -37,5 +37,15 @@ public class MarkdownConverter {
|
||||||
public String code;
|
public String code;
|
||||||
public int position;
|
public int position;
|
||||||
public URLSpan urlSpan;
|
public URLSpan urlSpan;
|
||||||
|
|
||||||
|
public int regexPosition(List<MarkdownItem> markdownItems) {
|
||||||
|
int position = 0;
|
||||||
|
for (MarkdownItem markdownItem : markdownItems) {
|
||||||
|
if (markdownItem.code.equals(code) && position <= this.position) {
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return position;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import android.view.View;
|
||||||
import android.webkit.URLUtil;
|
import android.webkit.URLUtil;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
@ -71,6 +72,7 @@ import java.util.regex.Pattern;
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
|
||||||
import app.fedilab.android.BaseMainActivity;
|
import app.fedilab.android.BaseMainActivity;
|
||||||
|
import app.fedilab.android.MySuperGrammerLocator;
|
||||||
import app.fedilab.android.R;
|
import app.fedilab.android.R;
|
||||||
import app.fedilab.android.activities.MainActivity;
|
import app.fedilab.android.activities.MainActivity;
|
||||||
import app.fedilab.android.databinding.PopupLinksBinding;
|
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 app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM;
|
||||||
import es.dmoral.toasty.Toasty;
|
import es.dmoral.toasty.Toasty;
|
||||||
import io.noties.markwon.Markwon;
|
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 {
|
public class SpannableHelper {
|
||||||
|
|
||||||
|
@ -149,44 +155,56 @@ public class SpannableHelper {
|
||||||
} else {
|
} else {
|
||||||
initialContent = new SpannableString(text);
|
initialContent = new SpannableString(text);
|
||||||
}
|
}
|
||||||
|
boolean markdownSupport = sharedpreferences.getBoolean(context.getString(R.string.SET_MARKDOWN_SUPPORT), true);
|
||||||
//Get all links
|
//Get all links
|
||||||
MarkdownConverter markdownConverter = new MarkdownConverter();
|
SpannableStringBuilder content;
|
||||||
markdownConverter.markdownItems = new ArrayList<>();
|
if (markdownSupport) {
|
||||||
int next;
|
MarkdownConverter markdownConverter = new MarkdownConverter();
|
||||||
int position = 0;
|
markdownConverter.markdownItems = new ArrayList<>();
|
||||||
for (int i = 0; i < initialContent.length(); i = next) {
|
int next;
|
||||||
// find the next span transition
|
int position = 0;
|
||||||
next = initialContent.nextSpanTransition(i, initialContent.length(), URLSpan.class);
|
for (int i = 0; i < initialContent.length(); i = next) {
|
||||||
MarkdownConverter.MarkdownItem markdownItem = new MarkdownConverter.MarkdownItem();
|
// find the next span transition
|
||||||
markdownItem.code = initialContent.subSequence(i, next).toString();
|
next = initialContent.nextSpanTransition(i, initialContent.length(), URLSpan.class);
|
||||||
|
MarkdownConverter.MarkdownItem markdownItem = new MarkdownConverter.MarkdownItem();
|
||||||
|
markdownItem.code = initialContent.subSequence(i, next).toString();
|
||||||
|
|
||||||
markdownItem.position = position;
|
markdownItem.position = position;
|
||||||
// get all spans in this range
|
// get all spans in this range
|
||||||
URLSpan[] spans = initialContent.getSpans(i, next, URLSpan.class);
|
URLSpan[] spans = initialContent.getSpans(i, next, URLSpan.class);
|
||||||
if (spans != null && spans.length > 0) {
|
if (spans != null && spans.length > 0) {
|
||||||
markdownItem.urlSpan = spans[0];
|
markdownItem.urlSpan = spans[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markdownItem.code.trim().length() > 0) {
|
||||||
|
markdownConverter.markdownItems.add(markdownItem);
|
||||||
|
position++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (markdownItem.code.trim().length() > 0) {
|
final Markwon markwon = Markwon.builder(context)
|
||||||
markdownConverter.markdownItems.add(markdownItem);
|
.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++;
|
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);
|
URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class);
|
||||||
//Loop through links
|
//Loop through links
|
||||||
|
@ -336,6 +354,23 @@ public class SpannableHelper {
|
||||||
return trimSpannable(new SpannableStringBuilder(content));
|
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) {
|
private static void makeLinks(Context context, SpannableStringBuilder content, String url, int start, int end) {
|
||||||
String newUrl = url;
|
String newUrl = url;
|
||||||
|
|
|
@ -1156,6 +1156,7 @@
|
||||||
|
|
||||||
<string name="SET_GROUP_REBLOGS" translatable="false">SET_GROUP_REBLOGS</string>
|
<string name="SET_GROUP_REBLOGS" translatable="false">SET_GROUP_REBLOGS</string>
|
||||||
<string name="SET_BOOST_ORIGINAL_DATE" translatable="false">SET_BOOST_ORIGINAL_DATE</string>
|
<string name="SET_BOOST_ORIGINAL_DATE" translatable="false">SET_BOOST_ORIGINAL_DATE</string>
|
||||||
|
<string name="SET_MARKDOWN_SUPPORT" translatable="false">SET_MARKDOWN_SUPPORT</string>
|
||||||
<string name="SET_TRUNCATE_LINKS" translatable="false">SET_TRUNCATE_LINKS</string>
|
<string name="SET_TRUNCATE_LINKS" translatable="false">SET_TRUNCATE_LINKS</string>
|
||||||
<string name="SET_TRUNCATE_LINKS_MAX" translatable="false">SET_TRUNCATE_LINKS_MAX</string>
|
<string name="SET_TRUNCATE_LINKS_MAX" translatable="false">SET_TRUNCATE_LINKS_MAX</string>
|
||||||
|
|
||||||
|
@ -1907,6 +1908,7 @@
|
||||||
<string name="also_followed_by">Followed by:</string>
|
<string name="also_followed_by">Followed by:</string>
|
||||||
<string name="Directory">Directory</string>
|
<string name="Directory">Directory</string>
|
||||||
<string name="boost_original_date">Display original date for boosts</string>
|
<string name="boost_original_date">Display original date for boosts</string>
|
||||||
|
<string name="markdown_support">Markdown support</string>
|
||||||
<string name="set_disable_release_notes">Disable release notes</string>
|
<string name="set_disable_release_notes">Disable release notes</string>
|
||||||
<string name="set_disable_release_notes_indication">When a new version is published, you will not be alerted inside the app.</string>
|
<string name="set_disable_release_notes_indication">When a new version is published, you will not be alerted inside the app.</string>
|
||||||
<string name="formula">Formula</string>
|
<string name="formula">Formula</string>
|
||||||
|
|
|
@ -50,6 +50,13 @@
|
||||||
app:singleLineTitle="false"
|
app:singleLineTitle="false"
|
||||||
app:title="@string/boost_original_date" />
|
app:title="@string/boost_original_date" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="true"
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
app:key="@string/SET_MARKDOWN_SUPPORT"
|
||||||
|
app:singleLineTitle="false"
|
||||||
|
app:title="@string/markdown_support" />
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
|
|
Loading…
Reference in a new issue