From 5fbc3a7224a2f4e2e383073280dd79751b8df53b Mon Sep 17 00:00:00 2001 From: Docile-Alligator <25734209+Docile-Alligator@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:27:23 -0500 Subject: Start implementing showing emotes in comments. --- .../activities/CommentActivity.java | 5 +- .../activities/FullMarkdownActivity.java | 5 +- .../infinityforreddit/activities/WikiActivity.java | 5 +- .../CommentsListingRecyclerViewAdapter.java | 7 +- .../adapters/CommentsRecyclerViewAdapter.java | 7 +- .../adapters/PostDetailRecyclerViewAdapter.java | 7 +- .../adapters/RulesRecyclerViewAdapter.java | 6 +- .../infinityforreddit/comment/ParseComment.java | 53 +++-- .../fragments/SidebarFragment.java | 5 +- .../infinityforreddit/markdown/Emote.java | 34 +++ .../markdown/EmoteCloseBracketInlineProcessor.java | 156 ++++++++++++++ .../markdown/EmoteInlineProcessor.java | 32 +++ .../infinityforreddit/markdown/EmotePlugin.java | 230 +++++++++++++++++++++ .../markdown/EmoteSpanFactory.java | 29 +++ .../markdown/ImageAndGifBlockParser.java | 2 +- .../infinityforreddit/markdown/MarkdownUtils.java | 6 + .../infinityforreddit/utils/Utils.java | 2 +- 17 files changed, 559 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/Emote.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteCloseBracketInlineProcessor.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteInlineProcessor.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmotePlugin.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteSpanFactory.java (limited to 'app/src/main/java/ml/docilealligator') diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java index 6096f1c4..8ec61c8a 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java @@ -65,6 +65,7 @@ import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed; import ml.docilealligator.infinityforreddit.databinding.ActivityCommentBinding; import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent; +import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin; import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils; @@ -210,9 +211,11 @@ public class CommentActivity extends BaseActivity implements UploadImageEnabledA builder.linkColor(linkColor); } }; + EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor(); ImageAndGifPlugin imageAndGifPlugin = new ImageAndGifPlugin(); Markwon postBodyMarkwon = MarkdownUtils.createFullRedditMarkwon(this, - miscPlugin, imageAndGifPlugin, parentTextColor, parentSpoilerBackgroundColor, null); + miscPlugin, emoteCloseBracketInlineProcessor, imageAndGifPlugin, parentTextColor, + parentSpoilerBackgroundColor, null); MarkwonAdapter markwonAdapter = MarkdownUtils.createTablesAdapter(new ImageAndGifEntry(this, mGlide, new ImageAndGifEntry.OnItemClickListener() { @Override public void onItemClick(MediaMetadata mediaMetadata) { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java index 92d6589c..07de032d 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java @@ -45,6 +45,7 @@ import ml.docilealligator.infinityforreddit.customviews.SwipeLockInterface; import ml.docilealligator.infinityforreddit.customviews.SwipeLockLinearLayoutManager; import ml.docilealligator.infinityforreddit.customviews.slidr.Slidr; import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent; +import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin; import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils; @@ -142,9 +143,11 @@ public class FullMarkdownActivity extends BaseActivity { builder.linkColor(linkColor); } }; + EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor(); ImageAndGifPlugin imageAndGifPlugin = new ImageAndGifPlugin(); Markwon markwon = MarkdownUtils.createFullRedditMarkwon(this, - miscPlugin, imageAndGifPlugin, markdownColor, spoilerBackgroundColor, null); + miscPlugin, emoteCloseBracketInlineProcessor, imageAndGifPlugin, markdownColor, + spoilerBackgroundColor, null); MarkwonAdapter markwonAdapter = MarkdownUtils.createTablesAdapter(new ImageAndGifEntry(this, Glide.with(this), new ImageAndGifEntry.OnItemClickListener() { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java index 16c1c748..10cbb519 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java @@ -53,6 +53,7 @@ import ml.docilealligator.infinityforreddit.customviews.SwipeLockInterface; import ml.docilealligator.infinityforreddit.customviews.SwipeLockLinearLayoutManager; import ml.docilealligator.infinityforreddit.customviews.slidr.Slidr; import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent; +import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin; import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils; @@ -98,6 +99,7 @@ public class WikiActivity extends BaseActivity { @Inject CustomThemeWrapper mCustomThemeWrapper; private String wikiMarkdown; + private EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor; private ImageAndGifPlugin imageAndGifPlugin; private Markwon markwon; private MarkwonAdapter markwonAdapter; @@ -179,9 +181,10 @@ public class WikiActivity extends BaseActivity { urlMenuBottomSheetFragment.show(getSupportFragmentManager(), null); return true; }; + emoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor(); imageAndGifPlugin = new ImageAndGifPlugin(); markwon = MarkdownUtils.createFullRedditMarkwon(this, - miscPlugin, imageAndGifPlugin, markdownColor, spoilerBackgroundColor, onLinkLongClickListener); + miscPlugin, emoteCloseBracketInlineProcessor, imageAndGifPlugin, markdownColor, spoilerBackgroundColor, onLinkLongClickListener); markwonAdapter = MarkdownUtils.createTablesAdapter(new ImageAndGifEntry(this, mGlide, new ImageAndGifEntry.OnItemClickListener() { diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java index 5abbbe8c..d947a370 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java @@ -58,6 +58,7 @@ import ml.docilealligator.infinityforreddit.customviews.SpoilerOnClickTextView; import ml.docilealligator.infinityforreddit.customviews.SwipeLockInterface; import ml.docilealligator.infinityforreddit.customviews.SwipeLockLinearLayoutManager; import ml.docilealligator.infinityforreddit.databinding.ItemCommentBinding; +import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry; import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin; import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils; @@ -84,6 +85,7 @@ public class CommentsListingRecyclerViewAdapter extends PagedListAdapter { private BaseActivity activity; + private EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor; private ImageAndGifPlugin imageAndGifPlugin; private Markwon markwon; @Nullable @@ -87,9 +89,11 @@ public class RulesRecyclerViewAdapter extends RecyclerView.Adapter mediaMetadataMap; + + @Override + public char specialCharacter() { + return ']'; + } + + @Override + protected Node parse() { + index++; + int startIndex = index; + + // Get previous `[` or `![` + Bracket opener = lastBracket(); + if (opener == null) { + // No matching opener, just return a literal. + return text("]"); + } + + if (!opener.allowed) { + // Matching opener but it's not allowed, just return a literal. + removeLastBracket(); + return text("]"); + } + + // Check to see if we have a link/image + + String dest = null; + String title = null; + boolean isLinkOrImage = false; + + // Maybe a inline link like `[foo](/uri "title")` + if (peek() == '(') { + index++; + spnl(); + if ((dest = parseLinkDestination()) != null) { + spnl(); + // title needs a whitespace before + if (WHITESPACE.matcher(input.substring(index - 1, index)).matches()) { + title = parseLinkTitle(); + spnl(); + } + if (peek() == ')') { + index++; + isLinkOrImage = true; + } else { + index = startIndex; + } + } + } + + // Maybe a reference link like `[foo][bar]`, `[foo][]` or `[foo]` + if (!isLinkOrImage) { + + // See if there's a link label like `[bar]` or `[]` + int beforeLabel = index; + parseLinkLabel(); + int labelLength = index - beforeLabel; + String ref = null; + if (labelLength > 2) { + ref = input.substring(beforeLabel, beforeLabel + labelLength); + } else if (!opener.bracketAfter) { + // If the second label is empty `[foo][]` or missing `[foo]`, then the first label is the reference. + // But it can only be a reference when there's no (unescaped) bracket in it. + // If there is, we don't even need to try to look up the reference. This is an optimization. + ref = input.substring(opener.index, startIndex); + } + + if (ref != null) { + String label = Escaping.normalizeReference(ref); + LinkReferenceDefinition definition = context.getLinkReferenceDefinition(label); + if (definition != null) { + dest = definition.getDestination(); + title = definition.getTitle(); + isLinkOrImage = true; + } + } + } + + if (isLinkOrImage) { + // If we got here, open is a potential opener + if (mediaMetadataMap == null) { + index = startIndex; + removeLastBracket(); + + return text("]"); + } + MediaMetadata mediaMetadata = mediaMetadataMap.get(dest); + if (mediaMetadata == null) { + index = startIndex; + removeLastBracket(); + + return text("]"); + } + + Node linkOrImage = opener.image ? new Emote(mediaMetadata, title) : new Link(dest, title); + + Node node = opener.node.getNext(); + while (node != null) { + Node next = node.getNext(); + linkOrImage.appendChild(node); + node = next; + } + + // Process delimiters such as emphasis inside link/image + processDelimiters(opener.previousDelimiter); + mergeChildTextNodes(linkOrImage); + // We don't need the corresponding text node anymore, we turned it into a link/image node + opener.node.unlink(); + removeLastBracket(); + + // Links within links are not allowed. We found this link, so there can be no other link around it. + if (!opener.image) { + Bracket bracket = lastBracket(); + while (bracket != null) { + if (!bracket.image) { + // Disallow link opener. It will still get matched, but will not result in a link. + bracket.allowed = false; + } + bracket = bracket.previous; + } + } + + return linkOrImage; + + } else { // no link or image + index = startIndex; + removeLastBracket(); + + return text("]"); + } + } + + public void setMediaMetadataMap(Map mediaMetadataMap) { + this.mediaMetadataMap = mediaMetadataMap; + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteInlineProcessor.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteInlineProcessor.java new file mode 100644 index 00000000..fcffc530 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteInlineProcessor.java @@ -0,0 +1,32 @@ +package ml.docilealligator.infinityforreddit.markdown; + +import org.commonmark.internal.Bracket; +import org.commonmark.node.Node; +import org.commonmark.node.Text; + +import io.noties.markwon.inlineparser.InlineProcessor; + +public class EmoteInlineProcessor extends InlineProcessor { + @Override + public char specialCharacter() { + return '!'; + } + + @Override + protected Node parse() { + int startIndex = index; + index++; + if (peek() == '[') { + index++; + + Text node = text("!["); + + // Add entry to stack for this opener + addBracket(Bracket.image(node, startIndex + 1, lastBracket(), lastDelimiter())); + + return node; + } else { + return null; + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmotePlugin.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmotePlugin.java new file mode 100644 index 00000000..24ae1ad3 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmotePlugin.java @@ -0,0 +1,230 @@ +package ml.docilealligator.infinityforreddit.markdown; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.Spanned; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.request.target.CustomTarget; +import com.bumptech.glide.request.target.Target; +import com.bumptech.glide.request.transition.Transition; + +import org.commonmark.node.Link; +import org.commonmark.node.Node; + +import java.util.HashMap; +import java.util.Map; + +import io.noties.markwon.AbstractMarkwonPlugin; +import io.noties.markwon.MarkwonConfiguration; +import io.noties.markwon.MarkwonSpansFactory; +import io.noties.markwon.MarkwonVisitor; +import io.noties.markwon.RenderProps; +import io.noties.markwon.SpanFactory; +import io.noties.markwon.image.AsyncDrawable; +import io.noties.markwon.image.AsyncDrawableLoader; +import io.noties.markwon.image.AsyncDrawableScheduler; +import io.noties.markwon.image.DrawableUtils; +import io.noties.markwon.image.ImageProps; +import io.noties.markwon.image.ImageSpanFactory; +import ml.docilealligator.infinityforreddit.MediaMetadata; + +public class EmotePlugin extends AbstractMarkwonPlugin { + public interface GlideStore { + + @NonNull + RequestBuilder load(@NonNull AsyncDrawable drawable); + + void cancel(@NonNull Target target); + } + + @NonNull + public static EmotePlugin create(@NonNull final Context context) { + // @since 4.5.0 cache RequestManager + // sometimes `cancel` would be called after activity is destroyed, + // so `Glide.with(context)` will throw an exception + return create(Glide.with(context)); + } + + @NonNull + public static EmotePlugin create(@NonNull final RequestManager requestManager) { + return create(new GlideStore() { + @NonNull + @Override + public RequestBuilder load(@NonNull AsyncDrawable drawable) { + return requestManager.load(drawable.getDestination()); + } + + @Override + public void cancel(@NonNull Target target) { + requestManager.clear(target); + } + }); + } + + @NonNull + public static EmotePlugin create(@NonNull GlideStore glideStore) { + return new EmotePlugin(glideStore); + } + + private final GlideAsyncDrawableLoader glideAsyncDrawableLoader; + + @SuppressWarnings("WeakerAccess") + EmotePlugin(@NonNull GlideStore glideStore) { + this.glideAsyncDrawableLoader = new GlideAsyncDrawableLoader(glideStore); + } + + @Override + public void configureSpansFactory(@NonNull MarkwonSpansFactory.Builder builder) { + builder.setFactory(Emote.class, new ImageSpanFactory()); + } + + @Override + public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) { + builder.asyncDrawableLoader(glideAsyncDrawableLoader); + } + + @Override + public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { + builder.on(Emote.class, new MarkwonVisitor.NodeVisitor() { + @Override + public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emote emote) { + // if there is no image spanFactory, ignore + final SpanFactory spanFactory = visitor.configuration().spansFactory().get(Emote.class); + if (spanFactory == null) { + visitor.visitChildren(emote); + return; + } + + final int length = visitor.length(); + + visitor.visitChildren(emote); + + // we must check if anything _was_ added, as we need at least one char to render + if (length == visitor.length()) { + visitor.builder().append('\uFFFC'); + } + + final MarkwonConfiguration configuration = visitor.configuration(); + + final Node parent = emote.getParent(); + final boolean link = parent instanceof Link; + + final String destination = configuration + .imageDestinationProcessor() + .process(emote.getMediaMetadata().original.url); + + final RenderProps props = visitor.renderProps(); + + // apply image properties + // Please note that we explicitly set IMAGE_SIZE to null as we do not clear + // properties after we applied span (we could though) + ImageProps.DESTINATION.set(props, destination); + ImageProps.REPLACEMENT_TEXT_IS_LINK.set(props, link); + ImageProps.IMAGE_SIZE.set(props, null); + + visitor.setSpans(length, spanFactory.getSpans(configuration, props)); + } + }); + } + + @Override + public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) { + AsyncDrawableScheduler.unschedule(textView); + } + + @Override + public void afterSetText(@NonNull TextView textView) { + AsyncDrawableScheduler.schedule(textView); + } + + public void setMediaMetadataMap(Map mediaMetadataMap) { + + } + + private static class GlideAsyncDrawableLoader extends AsyncDrawableLoader { + + private final GlideStore glideStore; + private final Map> cache = new HashMap<>(2); + + GlideAsyncDrawableLoader(@NonNull GlideStore glideStore) { + this.glideStore = glideStore; + } + + @Override + public void load(@NonNull AsyncDrawable drawable) { + final Target target = new AsyncDrawableTarget(drawable); + cache.put(drawable, target); + glideStore.load(drawable) + .into(target); + } + + @Override + public void cancel(@NonNull AsyncDrawable drawable) { + final Target target = cache.remove(drawable); + if (target != null) { + glideStore.cancel(target); + } + } + + @Nullable + @Override + public Drawable placeholder(@NonNull AsyncDrawable drawable) { + return null; + } + + private class AsyncDrawableTarget extends CustomTarget { + + private final AsyncDrawable drawable; + + AsyncDrawableTarget(@NonNull AsyncDrawable drawable) { + this.drawable = drawable; + } + + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + if (cache.remove(drawable) != null) { + if (drawable.isAttached()) { + DrawableUtils.applyIntrinsicBoundsIfEmpty(resource); + drawable.setResult(resource); + } + } + } + + @Override + public void onLoadStarted(@Nullable Drawable placeholder) { + if (placeholder != null + && drawable.isAttached()) { + DrawableUtils.applyIntrinsicBoundsIfEmpty(placeholder); + drawable.setResult(placeholder); + } + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + if (cache.remove(drawable) != null) { + if (errorDrawable != null + && drawable.isAttached()) { + DrawableUtils.applyIntrinsicBoundsIfEmpty(errorDrawable); + drawable.setResult(errorDrawable); + } + } + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + // we won't be checking if target is still present as cancellation + // must remove target anyway + if (drawable.isAttached()) { + drawable.clearResult(); + } + } + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteSpanFactory.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteSpanFactory.java new file mode 100644 index 00000000..c70488b8 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteSpanFactory.java @@ -0,0 +1,29 @@ +package ml.docilealligator.infinityforreddit.markdown; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import io.noties.markwon.MarkwonConfiguration; +import io.noties.markwon.RenderProps; +import io.noties.markwon.SpanFactory; +import io.noties.markwon.image.AsyncDrawable; +import io.noties.markwon.image.AsyncDrawableSpan; +import io.noties.markwon.image.ImageProps; + +public class EmoteSpanFactory implements SpanFactory { + @Nullable + @Override + public Object getSpans(@NonNull MarkwonConfiguration configuration, @NonNull RenderProps props) { + return new AsyncDrawableSpan( + configuration.theme(), + new AsyncDrawable( + ImageProps.DESTINATION.require(props), + configuration.asyncDrawableLoader(), + configuration.imageSizeResolver(), + ImageProps.IMAGE_SIZE.get(props) + ), + AsyncDrawableSpan.ALIGN_BOTTOM, + ImageProps.REPLACEMENT_TEXT_IS_LINK.get(props, false) + ); + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/ImageAndGifBlockParser.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/ImageAndGifBlockParser.java index d45aa177..883e8a8d 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/ImageAndGifBlockParser.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/ImageAndGifBlockParser.java @@ -33,7 +33,7 @@ public class ImageAndGifBlockParser extends AbstractBlockParser { } public static class Factory extends AbstractBlockParserFactory { - private final Pattern redditPreviewPattern = Pattern.compile("!\\[img]\\(https://preview.redd.it/\\w+.(jpg|png)((\\?+[-a-zA-Z0-9()@:%_+.~#?&/=]*)|)\\)"); + private final Pattern redditPreviewPattern = Pattern.compile("!\\[img]\\(https://preview.redd.it/\\w+.(jpg|png|jpeg)((\\?+[-a-zA-Z0-9()@:%_+.~#?&/=]*)|)\\)"); private final Pattern gifPattern = Pattern.compile("!\\[gif]\\(giphy\\|\\w+(\\|downsized)?\\)"); private Map mediaMetadataMap; private final int fromIndex = "![img](https://preview.redd.it/".length(); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java index 5ff708ed..1b4e92b0 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java @@ -12,6 +12,7 @@ import io.noties.markwon.Markwon; import io.noties.markwon.MarkwonPlugin; import io.noties.markwon.ext.strikethrough.StrikethroughPlugin; import io.noties.markwon.inlineparser.BangInlineProcessor; +import io.noties.markwon.inlineparser.CloseBracketInlineProcessor; import io.noties.markwon.inlineparser.HtmlInlineProcessor; import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin; import io.noties.markwon.linkify.LinkifyPlugin; @@ -31,6 +32,7 @@ public class MarkdownUtils { @NonNull public static Markwon createFullRedditMarkwon(@NonNull Context context, @NonNull MarkwonPlugin miscPlugin, + @NonNull EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor, @NonNull ImageAndGifPlugin imageAndGifPlugin, int markdownColor, int spoilerBackgroundColor, @@ -39,6 +41,9 @@ public class MarkdownUtils { .usePlugin(MarkwonInlineParserPlugin.create(plugin -> { plugin.excludeInlineProcessor(HtmlInlineProcessor.class); plugin.excludeInlineProcessor(BangInlineProcessor.class); + plugin.excludeInlineProcessor(CloseBracketInlineProcessor.class); + plugin.addInlineProcessor(new EmoteInlineProcessor()); + plugin.addInlineProcessor(emoteCloseBracketInlineProcessor); })) .usePlugin(miscPlugin) .usePlugin(SuperscriptPlugin.create()) @@ -49,6 +54,7 @@ public class MarkdownUtils { .setOnLinkLongClickListener(onLinkLongClickListener))) .usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS)) .usePlugin(imageAndGifPlugin) + .usePlugin(EmotePlugin.create(context)) .usePlugin(TableEntryPlugin.create(context)) .build(); } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java index 0efed43b..d6b3ed6c 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java @@ -75,7 +75,7 @@ public final class Utils { Pattern.compile("!\\[gif]\\(giphy\\|\\w+\\)"), Pattern.compile("!\\[gif]\\(giphy\\|\\w+\\|downsized\\)"), Pattern.compile("!\\[gif]\\(emote\\|\\w+\\|\\w+\\)"), - Pattern.compile("https://preview.redd.it/\\w+.(jpg|png)((\\?+[-a-zA-Z0-9()@:%_+.~#?&/=]*)|)") + Pattern.compile("https://preview.redd.it/\\w+.(jpg|png|jpeg)((\\?+[-a-zA-Z0-9()@:%_+.~#?&/=]*)|)") }; public static String modifyMarkdown(String markdown) { -- cgit v1.2.3