aboutsummaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorDocile-Alligator <chineseperson5@gmail.com>2021-11-27 12:19:44 +0000
committerGitHub <noreply@github.com>2021-11-27 12:19:44 +0000
commit091dd5180118e0c5fc3c9fa5d4c7248fcffa0120 (patch)
treeb0bc69fae6118d6cad9d95e0d5f130f2244cbe05 /app/src/main
parent1b39925cf8f050597e8c98266b724d2cb0334223 (diff)
parent209a874f57f37fd7b32daa5df2c8951df0c940f4 (diff)
downloadinfinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.tar
infinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.tar.gz
infinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.tar.bz2
infinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.tar.lz
infinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.tar.xz
infinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.tar.zst
infinity-for-reddit-091dd5180118e0c5fc3c9fa5d4c7248fcffa0120.zip
Merge pull request #537 from scria1000/more-regex-fix
Refactor spoiler parsing into its own class
Diffstat (limited to '')
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditCommentActivity.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditPostActivity.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java50
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java50
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java55
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java57
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MessageRecyclerViewAdapter.java52
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java50
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PrivateMessagesDetailRecyclerViewAdapter.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/CopyTextBottomSheetFragment.java1
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/BlockQuoteWithExceptionParser.java85
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerParserPlugin.java202
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerSpan.java64
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SuperscriptInlineProcessor.java (renamed from app/src/main/java/ml/docilealligator/infinityforreddit/utils/SuperscriptInlineProcessor.java)2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java1
18 files changed, 382 insertions, 299 deletions
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 ffb9173b..7680f2fd 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java
@@ -72,7 +72,7 @@ import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit;
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditCommentActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditCommentActivity.java
index cb03370b..4f4b81ec 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditCommentActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditCommentActivity.java
@@ -132,7 +132,7 @@ public class EditCommentActivity extends BaseActivity implements UploadImageEnab
mFullName = getIntent().getStringExtra(EXTRA_FULLNAME);
mAccessToken = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCESS_TOKEN, null);
- mCommentContent = getIntent().getStringExtra(EXTRA_CONTENT).replaceAll("&gt;",">");
+ mCommentContent = getIntent().getStringExtra(EXTRA_CONTENT);
contentEditText.setText(mCommentContent);
if (savedInstanceState != null) {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditPostActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditPostActivity.java
index 16e91ebc..0d22d9b3 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditPostActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/EditPostActivity.java
@@ -141,7 +141,7 @@ public class EditPostActivity extends BaseActivity implements UploadImageEnabled
mFullName = getIntent().getStringExtra(EXTRA_FULLNAME);
mAccessToken = mCurrentAccountSharedPreferences.getString(SharedPreferencesUtils.ACCESS_TOKEN, null);
titleTextView.setText(getIntent().getStringExtra(EXTRA_TITLE));
- mPostContent = getIntent().getStringExtra(EXTRA_CONTENT).replaceAll("&gt;",">");;
+ mPostContent = getIntent().getStringExtra(EXTRA_CONTENT);
contentEditText.setText(mPostContent);
if (savedInstanceState != null) {
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 32a5f452..acd1713c 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java
@@ -61,8 +61,10 @@ import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
import ml.docilealligator.infinityforreddit.customviews.MarkwonLinearLayoutManager;
import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerSpan;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
public class FullMarkdownActivity extends BaseActivity {
@@ -150,51 +152,6 @@ public class FullMarkdownActivity extends BaseActivity {
public String processMarkdown(@NonNull String markdown) {
return super.processMarkdown(Utils.fixSuperScript(markdown));
}
-
- @Override
- public void afterSetText(@NonNull TextView textView) {
- textView.setHighlightColor(Color.TRANSPARENT);
- SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
- Pattern spoilerPattern = Pattern.compile(">![\\S\\s]+?!<");
- Matcher matcher = spoilerPattern.matcher(markdownStringBuilder);
- int start = 0;
- boolean find = false;
- while (matcher.find(start)) {
- if (markdownStringBuilder.length() < 4
- || matcher.start() < 0
- || matcher.end() > markdownStringBuilder.length()) {
- break;
- }
- find = true;
- markdownStringBuilder.delete(matcher.end() - 2, matcher.end());
- markdownStringBuilder.delete(matcher.start(), matcher.start() + 2);
- ClickableSpan clickableSpan = new ClickableSpan() {
- private boolean isShowing = false;
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- if (isShowing) {
- super.updateDrawState(ds);
- ds.setColor(markdownColor);
- } else {
- ds.bgColor = spoilerBackgroundColor;
- ds.setColor(markdownColor);
- }
- ds.setUnderlineText(false);
- }
-
- @Override
- public void onClick(@NonNull View view) {
- isShowing = !isShowing;
- view.invalidate();
- }
- };
- markdownStringBuilder.setSpan(clickableSpan, matcher.start(), matcher.end() - 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- start = matcher.end() - 4;
- }
- if (find) {
- textView.setText(markdownStringBuilder);
- }
- }
@Override
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
@@ -217,6 +174,7 @@ public class FullMarkdownActivity extends BaseActivity {
builder.linkColor(linkColor);
}
})
+ .usePlugin(SpoilerParserPlugin.create(markdownColor, spoilerBackgroundColor))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
.usePlugin(TableEntryPlugin.create(this))
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 09fcffe6..4568cfe5 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java
@@ -67,9 +67,11 @@ import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
import ml.docilealligator.infinityforreddit.customviews.MarkwonLinearLayoutManager;
import ml.docilealligator.infinityforreddit.events.SwitchAccountEvent;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerSpan;
import ml.docilealligator.infinityforreddit.utils.JSONUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Call;
import retrofit2.Callback;
@@ -181,51 +183,6 @@ public class WikiActivity extends BaseActivity {
}
@Override
- public void afterSetText(@NonNull TextView textView) {
- textView.setHighlightColor(Color.TRANSPARENT);
- SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
- Pattern spoilerPattern = Pattern.compile(">![\\S\\s]+?!<");
- Matcher matcher = spoilerPattern.matcher(markdownStringBuilder);
- int start = 0;
- boolean find = false;
- while (matcher.find(start)) {
- if (markdownStringBuilder.length() < 4
- || matcher.start() < 0
- || matcher.end() > markdownStringBuilder.length()) {
- break;
- }
- find = true;
- markdownStringBuilder.delete(matcher.end() - 2, matcher.end());
- markdownStringBuilder.delete(matcher.start(), matcher.start() + 2);
- ClickableSpan clickableSpan = new ClickableSpan() {
- private boolean isShowing = false;
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- if (isShowing) {
- super.updateDrawState(ds);
- ds.setColor(markdownColor);
- } else {
- ds.bgColor = spoilerBackgroundColor;
- ds.setColor(markdownColor);
- }
- ds.setUnderlineText(false);
- }
-
- @Override
- public void onClick(@NonNull View view) {
- isShowing = !isShowing;
- view.invalidate();
- }
- };
- markdownStringBuilder.setSpan(clickableSpan, matcher.start(), matcher.end() - 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- start = matcher.end() - 4;
- }
- if (find) {
- textView.setText(markdownStringBuilder);
- }
- }
-
- @Override
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
textView.setTextColor(markdownColor);
}
@@ -245,6 +202,7 @@ public class WikiActivity extends BaseActivity {
builder.linkColor(linkColor);
}
})
+ .usePlugin(SpoilerParserPlugin.create(markdownColor, spoilerBackgroundColor))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
.usePlugin(TableEntryPlugin.create(this))
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 616b9c1b..40bce0d6 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java
@@ -63,9 +63,11 @@ import ml.docilealligator.infinityforreddit.comment.Comment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.CommentIndentationView;
import ml.docilealligator.infinityforreddit.customviews.SpoilerOnClickTextView;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerSpan;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit;
@@ -137,56 +139,6 @@ public class CommentsListingRecyclerViewAdapter extends PagedListAdapter<Comment
}
@Override
- public void afterSetText(@NonNull TextView textView) {
- textView.setHighlightColor(Color.TRANSPARENT);
- SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
- Pattern spoilerPattern = Pattern.compile(">![\\S\\s]+?!<");
- Matcher matcher = spoilerPattern.matcher(markdownStringBuilder);
- int start = 0;
- boolean find = false;
- while (matcher.find(start)) {
- if (markdownStringBuilder.length() < 4
- || matcher.start() < 0
- || matcher.end() > markdownStringBuilder.length()) {
- break;
- }
- find = true;
- markdownStringBuilder.delete(matcher.end() - 2, matcher.end());
- markdownStringBuilder.delete(matcher.start(), matcher.start() + 2);
- int matcherStart = matcher.start();
- int matcherEnd = matcher.end();
- ClickableSpan clickableSpan = new ClickableSpan() {
- private boolean isShowing = false;
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- if (isShowing) {
- super.updateDrawState(ds);
- ds.setColor(mCommentColor);
- } else {
- ds.bgColor = commentSpoilerBackgroundColor;
- ds.setColor(mCommentColor);
- }
- ds.setUnderlineText(false);
- }
-
- @Override
- public void onClick(@NonNull View view) {
- if (textView instanceof SpoilerOnClickTextView) {
- ((SpoilerOnClickTextView) textView).setSpoilerOnClick(true);
- }
- isShowing = !isShowing;
- view.invalidate();
- }
- };
- markdownStringBuilder.setSpan(clickableSpan, matcherStart, matcherEnd - 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- start = matcherEnd - 4;
- }
- if (find) {
- textView.setText(markdownStringBuilder);
- }
- }
-
- @Override
public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
builder.linkColor(customThemeWrapper.getLinkColor());
}
@@ -202,6 +154,7 @@ public class CommentsListingRecyclerViewAdapter extends PagedListAdapter<Comment
}
})
+ .usePlugin(SpoilerParserPlugin.create(mCommentColor, commentSpoilerBackgroundColor))
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
.usePlugin(StrikethroughPlugin.create())
.build();
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
index 5bd116ee..6e6d18d5 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
@@ -9,10 +9,6 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.style.ClickableSpan;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,8 +34,6 @@ import com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.Executor;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -50,6 +44,7 @@ import io.noties.markwon.core.MarkwonTheme;
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
import io.noties.markwon.html.HtmlPlugin;
import io.noties.markwon.html.tag.SuperScriptHandler;
+import io.noties.markwon.inlineparser.BackslashInlineProcessor;
import io.noties.markwon.inlineparser.BangInlineProcessor;
import io.noties.markwon.inlineparser.HtmlInlineProcessor;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
@@ -71,10 +66,11 @@ import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.CommentIndentationView;
import ml.docilealligator.infinityforreddit.customviews.SpoilerOnClickTextView;
import ml.docilealligator.infinityforreddit.fragments.ViewPostDetailFragment;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.post.Post;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit;
@@ -182,52 +178,6 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
}
@Override
- public void afterSetText(@NonNull TextView textView) {
- SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
- Pattern spoilerPattern = Pattern.compile(">![\\S\\s]+?!<");
- Matcher matcher = spoilerPattern.matcher(markdownStringBuilder);
- int start = 0;
- boolean find = false;
- while (matcher.find(start)) {
- if (markdownStringBuilder.length() < 4
- || matcher.start() < 0
- || matcher.end() > markdownStringBuilder.length()) {
- break;
- }
- find = true;
- markdownStringBuilder.delete(matcher.end() - 2, matcher.end());
- markdownStringBuilder.delete(matcher.start(), matcher.start() + 2);
- ClickableSpan clickableSpan = new ClickableSpan() {
- private boolean isShowing = false;
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- if (isShowing) {
- super.updateDrawState(ds);
- } else {
- ds.bgColor = commentSpoilerBackgroundColor;
- }
- ds.setColor(mCommentTextColor);
- ds.setUnderlineText(false);
- }
-
- @Override
- public void onClick(@NonNull View view) {
- if (textView instanceof SpoilerOnClickTextView) {
- ((SpoilerOnClickTextView) textView).setSpoilerOnClick(true);
- }
- isShowing = !isShowing;
- view.invalidate();
- }
- };
- markdownStringBuilder.setSpan(clickableSpan, matcher.start(), matcher.end() - 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- start = matcher.end() - 4;
- }
- if (find) {
- textView.setText(markdownStringBuilder);
- }
- }
-
- @Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.linkResolver((view, link) -> {
Intent intent = new Intent(mActivity, LinkResolverActivity.class);
@@ -243,6 +193,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
builder.linkColor(linkColor);
}
})
+ .usePlugin(SpoilerParserPlugin.create(mCommentTextColor, commentSpoilerBackgroundColor))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(MovementMethodPlugin.create(BetterLinkMovementMethod.newInstance().setOnLinkLongClickListener((textView, url) -> {
if (!activity.isDestroyed() && !activity.isFinishing()) {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MessageRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MessageRecyclerViewAdapter.java
index 095a36f7..7560aab4 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MessageRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/MessageRecyclerViewAdapter.java
@@ -46,10 +46,12 @@ import ml.docilealligator.infinityforreddit.activities.LinkResolverActivity;
import ml.docilealligator.infinityforreddit.activities.ViewPrivateMessagesActivity;
import ml.docilealligator.infinityforreddit.activities.ViewUserDetailActivity;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerSpan;
import ml.docilealligator.infinityforreddit.message.FetchMessage;
import ml.docilealligator.infinityforreddit.message.Message;
import ml.docilealligator.infinityforreddit.message.ReadMessage;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit;
@@ -121,53 +123,6 @@ public class MessageRecyclerViewAdapter extends PagedListAdapter<Message, Recycl
}
@Override
- public void afterSetText(@NonNull TextView textView) {
- textView.setHighlightColor(Color.TRANSPARENT);
- SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
- Pattern spoilerPattern = Pattern.compile(">![\\S\\s]+?!<");
- Matcher matcher = spoilerPattern.matcher(markdownStringBuilder);
- int start = 0;
- boolean find = false;
- while (matcher.find(start)) {
- if (markdownStringBuilder.length() < 4
- || matcher.start() < 0
- || matcher.end() > markdownStringBuilder.length()) {
- break;
- }
- find = true;
- markdownStringBuilder.delete(matcher.end() - 2, matcher.end());
- markdownStringBuilder.delete(matcher.start(), matcher.start() + 2);
- int matcherStart = matcher.start();
- int matcherEnd = matcher.end();
- ClickableSpan clickableSpan = new ClickableSpan() {
- private boolean isShowing = false;
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- if (isShowing) {
- super.updateDrawState(ds);
- ds.setColor(mSecondaryTextColor);
- } else {
- ds.bgColor = spoilerBackgroundColor;
- ds.setColor(mSecondaryTextColor);
- }
- ds.setUnderlineText(false);
- }
-
- @Override
- public void onClick(@NonNull View view) {
- isShowing = !isShowing;
- view.invalidate();
- }
- };
- markdownStringBuilder.setSpan(clickableSpan, matcherStart, matcherEnd - 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- start = matcherEnd - 4;
- }
- if (find) {
- textView.setText(markdownStringBuilder);
- }
- }
-
- @Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.linkResolver((view, link) -> {
Intent intent = new Intent(mContext, LinkResolverActivity.class);
@@ -182,6 +137,7 @@ public class MessageRecyclerViewAdapter extends PagedListAdapter<Message, Recycl
builder.linkColor(customThemeWrapper.getLinkColor());
}
})
+ .usePlugin(SpoilerParserPlugin.create(mSecondaryTextColor, spoilerBackgroundColor))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
.build();
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
index 0eb69416..1fa45219 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
@@ -111,11 +111,13 @@ import ml.docilealligator.infinityforreddit.customviews.AspectRatioGifImageView;
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
import ml.docilealligator.infinityforreddit.customviews.MarkwonLinearLayoutManager;
import ml.docilealligator.infinityforreddit.fragments.ViewPostDetailFragment;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
+import ml.docilealligator.infinityforreddit.markdown.SpoilerSpan;
import ml.docilealligator.infinityforreddit.post.Post;
import ml.docilealligator.infinityforreddit.post.PostPagingSource;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import pl.droidsonroids.gif.GifImageView;
import retrofit2.Retrofit;
@@ -247,51 +249,6 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
}
@Override
- public void afterSetText(@NonNull TextView textView) {
- textView.setHighlightColor(Color.TRANSPARENT);
- SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
- Pattern spoilerPattern = Pattern.compile(">![\\S\\s]+?!<");
- Matcher matcher = spoilerPattern.matcher(markdownStringBuilder);
- int start = 0;
- boolean find = false;
- while (matcher.find(start)) {
- if (markdownStringBuilder.length() < 4
- || matcher.start() < 0
- || matcher.end() > markdownStringBuilder.length()) {
- break;
- }
- find = true;
- markdownStringBuilder.delete(matcher.end() - 2, matcher.end());
- markdownStringBuilder.delete(matcher.start(), matcher.start() + 2);
- ClickableSpan clickableSpan = new ClickableSpan() {
- private boolean isShowing = false;
- @Override
- public void updateDrawState(@NonNull TextPaint ds) {
- if (isShowing) {
- super.updateDrawState(ds);
- ds.setColor(markdownColor);
- } else {
- ds.bgColor = postSpoilerBackgroundColor;
- ds.setColor(markdownColor);
- }
- ds.setUnderlineText(false);
- }
-
- @Override
- public void onClick(@NonNull View view) {
- isShowing = !isShowing;
- view.invalidate();
- }
- };
- markdownStringBuilder.setSpan(clickableSpan, matcher.start(), matcher.end() - 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- start = matcher.end() - 4;
- }
- if (find) {
- textView.setText(markdownStringBuilder);
- }
- }
-
- @Override
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
textView.setTextColor(markdownColor);
textView.setOnLongClickListener(view -> {
@@ -323,6 +280,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
builder.linkColor(linkColor);
}
})
+ .usePlugin(SpoilerParserPlugin.create(markdownColor, postSpoilerBackgroundColor))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(MovementMethodPlugin.create(BetterLinkMovementMethod.linkify(Linkify.WEB_URLS, activity).setOnLinkLongClickListener((textView, url) -> {
if (activity != null && !activity.isDestroyed() && !activity.isFinishing()) {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PrivateMessagesDetailRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PrivateMessagesDetailRecyclerViewAdapter.java
index e357771b..180b74be 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PrivateMessagesDetailRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PrivateMessagesDetailRecyclerViewAdapter.java
@@ -41,7 +41,7 @@ import ml.docilealligator.infinityforreddit.activities.ViewUserDetailActivity;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.message.Message;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
public class PrivateMessagesDetailRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java
index 294733a1..d82fcf8d 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java
@@ -35,7 +35,7 @@ import ml.docilealligator.infinityforreddit.Rule;
import ml.docilealligator.infinityforreddit.activities.LinkResolverActivity;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.UrlMenuBottomSheetFragment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
public class RulesRecyclerViewAdapter extends RecyclerView.Adapter<RulesRecyclerViewAdapter.RuleViewHolder> {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/CopyTextBottomSheetFragment.java b/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/CopyTextBottomSheetFragment.java
index 4264f36a..e30156dc 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/CopyTextBottomSheetFragment.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/CopyTextBottomSheetFragment.java
@@ -74,7 +74,6 @@ public class CopyTextBottomSheetFragment extends LandscapeExpandedRoundedBottomS
if (markdownText != null) {
//markdownText = markdownText.replaceAll("<sup>", "^").replaceAll("</sup>", "");
- markdownText = markdownText.replaceAll("&gt;", ">");
copyMarkdownTextView.setOnClickListener(view -> {
showCopyDialog(markdownText);
dismiss();
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java b/app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java
index 3f22a64e..bea0c4ed 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java
@@ -58,7 +58,7 @@ import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFi
import ml.docilealligator.infinityforreddit.subreddit.FetchSubredditData;
import ml.docilealligator.infinityforreddit.subreddit.SubredditData;
import ml.docilealligator.infinityforreddit.subreddit.SubredditViewModel;
-import ml.docilealligator.infinityforreddit.utils.SuperscriptInlineProcessor;
+import ml.docilealligator.infinityforreddit.markdown.SuperscriptInlineProcessor;
import ml.docilealligator.infinityforreddit.utils.Utils;
import retrofit2.Retrofit;
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/BlockQuoteWithExceptionParser.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/BlockQuoteWithExceptionParser.java
new file mode 100644
index 00000000..f500bb93
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/BlockQuoteWithExceptionParser.java
@@ -0,0 +1,85 @@
+package ml.docilealligator.infinityforreddit.markdown;
+
+import org.commonmark.internal.util.Parsing;
+import org.commonmark.node.Block;
+import org.commonmark.node.BlockQuote;
+import org.commonmark.parser.block.AbstractBlockParser;
+import org.commonmark.parser.block.AbstractBlockParserFactory;
+import org.commonmark.parser.block.BlockContinue;
+import org.commonmark.parser.block.BlockStart;
+import org.commonmark.parser.block.MatchedBlockParser;
+import org.commonmark.parser.block.ParserState;
+
+// Parse and consume a block quote except when it's a spoiler opening
+public class BlockQuoteWithExceptionParser extends AbstractBlockParser {
+ private final BlockQuote block = new BlockQuote();
+
+ private static boolean isMarker(ParserState state, int index) {
+ CharSequence line = state.getLine();
+ return state.getIndent() < Parsing.CODE_BLOCK_INDENT && index < line.length() && line.charAt(index) == '>';
+ }
+
+ private static boolean isMarkerSpoiler(ParserState state, int index) {
+ CharSequence line = state.getLine();
+ int length = line.length();
+ try {
+ return state.getIndent() < Parsing.CODE_BLOCK_INDENT && index < length && line.charAt(index) == '>' && (index + 1) < length && line.charAt(index + 1) == '!';
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isContainer() {
+ return true;
+ }
+
+ @Override
+ public boolean canContain(Block block) {
+ return true;
+ }
+
+ @Override
+ public BlockQuote getBlock() {
+ return block;
+ }
+
+ @Override
+ public BlockContinue tryContinue(ParserState state) {
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ if (isMarker(state, nextNonSpace)) {
+ int newColumn = state.getColumn() + state.getIndent() + 1;
+ // optional following space or tab
+ if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
+ newColumn++;
+ }
+ return BlockContinue.atColumn(newColumn);
+ } else {
+ return BlockContinue.none();
+ }
+ }
+
+ public static class Factory extends AbstractBlockParserFactory {
+ public BlockStart tryStart(ParserState state, MatchedBlockParser matchedBlockParser) {
+ int nextNonSpace = state.getNextNonSpaceIndex();
+ // Potential for a spoiler opening
+ // We don't check for spoiler closing, neither does Reddit
+ if (isMarkerSpoiler(state, nextNonSpace)) {
+ // It might be a spoiler, don't consume
+ return BlockStart.none();
+ }
+ // Not a spoiler then
+ else if (isMarker(state, nextNonSpace)) {
+ int newColumn = state.getColumn() + state.getIndent() + 1;
+ // optional following space or tab
+ if (Parsing.isSpaceOrTab(state.getLine(), nextNonSpace + 1)) {
+ newColumn++;
+ }
+ return BlockStart.of(new BlockQuoteWithExceptionParser()).atColumn(newColumn);
+ } else {
+ return BlockStart.none();
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerParserPlugin.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerParserPlugin.java
new file mode 100644
index 00000000..1cca0cfe
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerParserPlugin.java
@@ -0,0 +1,202 @@
+package ml.docilealligator.infinityforreddit.markdown;
+
+import android.graphics.Color;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import org.commonmark.node.Block;
+import org.commonmark.node.BlockQuote;
+import org.commonmark.node.HtmlBlock;
+import org.commonmark.node.HtmlInline;
+import org.commonmark.parser.Parser;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import io.noties.markwon.AbstractMarkwonPlugin;
+import io.noties.markwon.core.CorePlugin;
+import io.noties.markwon.core.spans.CodeBlockSpan;
+import io.noties.markwon.core.spans.CodeSpan;
+
+public class SpoilerParserPlugin extends AbstractMarkwonPlugin {
+ private final int textColor;
+ private final int backgroundColor;
+
+ SpoilerParserPlugin(int textColor, int backgroundColor) {
+ this.textColor = textColor;
+ this.backgroundColor = backgroundColor;
+ }
+
+ public static SpoilerParserPlugin create(@NonNull int textColor, @NonNull int backgroundColor) {
+ return new SpoilerParserPlugin(textColor, backgroundColor);
+ }
+
+ @Override
+ public void configureParser(@NonNull Parser.Builder builder) {
+ builder.customBlockParserFactory(new BlockQuoteWithExceptionParser.Factory());
+
+ Set<Class<? extends Block>> blocks = CorePlugin.enabledBlockTypes();
+ blocks.remove(HtmlBlock.class);
+ blocks.remove(HtmlInline.class);
+ blocks.remove(BlockQuote.class);
+
+ builder.enabledBlockTypes(blocks);
+ }
+
+ @Override
+ public void afterSetText(@NonNull TextView textView) {
+ textView.setHighlightColor(Color.TRANSPARENT);
+
+ if(textView.getText().length() < 5) {
+ return;
+ }
+
+ SpannableStringBuilder markdownStringBuilder = new SpannableStringBuilder(textView.getText());
+
+ LinkedHashMap<Integer, Integer> spoilers = parse(markdownStringBuilder);
+ if(spoilers.size() == 0) {
+ return;
+ }
+ int offset = 2;
+
+ for (Map.Entry<Integer, Integer> entry : spoilers.entrySet()) {
+ int spoilerStart = entry.getKey() - offset;
+ int spoilerEnd = entry.getValue() - offset;
+
+ // Try not to set a spoiler span if it's inside a CodeSpan
+ CodeSpan[] codeSpans = markdownStringBuilder.getSpans(spoilerStart, spoilerEnd, CodeSpan.class);
+ CodeBlockSpan[] codeBlockSpans = markdownStringBuilder.getSpans(spoilerStart, spoilerEnd, CodeBlockSpan.class);
+
+ if (codeSpans.length == 0 && codeBlockSpans.length == 0) {
+ markdownStringBuilder.delete(spoilerStart, spoilerStart + 2);
+ markdownStringBuilder.delete(spoilerEnd, spoilerEnd + 2);
+ SpoilerSpan spoilerSpan = new SpoilerSpan(textColor, backgroundColor);
+ markdownStringBuilder.setSpan(spoilerSpan, spoilerStart, spoilerEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ offset += 4;
+ }
+
+ for (CodeSpan codeSpan : codeSpans) {
+ int spanBeginning = markdownStringBuilder.getSpanStart(codeSpan);
+ int spanEnd = markdownStringBuilder.getSpanEnd(codeSpan);
+ if (spoilerStart + 2 <= spanBeginning && spanEnd <= spoilerEnd + 2) {
+ markdownStringBuilder.delete(spoilerStart, spoilerStart + 2);
+ markdownStringBuilder.delete(spoilerEnd, spoilerEnd + 2);
+ SpoilerSpan spoilerSpan = new SpoilerSpan(textColor, backgroundColor);
+ markdownStringBuilder.setSpan(spoilerSpan, spoilerStart, spoilerEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ offset += 4;
+ } else {
+ break;
+ }
+ }
+
+ for (CodeBlockSpan codeBlockSpan : codeBlockSpans) {
+ int spanBeginning = markdownStringBuilder.getSpanStart(codeBlockSpan);
+ int spanEnd = markdownStringBuilder.getSpanEnd(codeBlockSpan);
+ if (spoilerStart + 2 <= spanBeginning && spanEnd <= spoilerEnd + 2) {
+ markdownStringBuilder.delete(spoilerStart, spoilerStart + 2);
+ markdownStringBuilder.delete(spoilerEnd, spoilerEnd + 2);
+ SpoilerSpan spoilerSpan = new SpoilerSpan(textColor, backgroundColor);
+ markdownStringBuilder.setSpan(spoilerSpan, spoilerStart, spoilerEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ offset += 4;
+ } else {
+ break;
+ }
+ }
+ }
+ if(offset > 2) {
+ textView.setText(markdownStringBuilder);
+ }
+ }
+
+ // Very naive implementation, needs to be improved for efficiency and edge cases
+ // Don't allow more than one new line after every non-blank line
+ // Try not to care about recursing spoilers, we just want the outermost spoiler because
+ // spoiler revealing-hiding breaks with recursing spoilers
+ private LinkedHashMap<Integer, Integer> parse(SpannableStringBuilder markdown) {
+ final int MAX_NEW_LINE = 1;
+ var openSpoilerStack = new Stack<Integer>();
+ var closedSpoilerMap = new LinkedHashMap<Integer, Integer>();
+ int variable_max_depth = calculateBalance(0, markdown) + 1;
+ int new_lines = 0;
+ int depth = 0;
+ for (int i = 0; i < markdown.length(); i++) {
+ if (markdown.charAt(i) == '\u2000' || markdown.charAt(i) == '\t') {
+ continue;
+ } else if (markdown.charAt(i) == '>' && (i + 1) < markdown.length() && markdown.charAt(i + 1) == '!') {
+ openSpoilerStack.push(i + 1);
+ depth++;
+ } else if (openSpoilerStack.size() > 0
+ && markdown.charAt(i) == '!' && (i + 1) < markdown.length()
+ && markdown.charAt(i + 1) == '<') {
+ var pos = i + 1;
+ for (int j = 0; j < depth; j++) {
+ if (!openSpoilerStack.isEmpty()) pos = openSpoilerStack.peek();
+ if (pos + 1 <= i) {
+ if (!openSpoilerStack.isEmpty()) pos = openSpoilerStack.peek();
+ break;
+ } else {
+ if (!openSpoilerStack.isEmpty()) pos = openSpoilerStack.pop();
+ }
+ }
+ if (depth <= variable_max_depth && pos + 1 <= i) //Spoiler content cannot be zero or less length
+ {
+ openSpoilerStack.clear();
+ closedSpoilerMap.put(pos + 1, i);
+ }
+ depth--;
+ } else if (markdown.charAt(i) == '\n') {
+ new_lines++;
+ if (openSpoilerStack.size() >= 1 && new_lines > MAX_NEW_LINE) {
+ openSpoilerStack.clear();
+ new_lines = 0;
+ depth = 0;
+ variable_max_depth = calculateBalance(i, markdown) + 1;
+ }
+ } else {
+ new_lines = 0;
+ }
+
+ if (openSpoilerStack.size() >= 32) // No
+ {
+ openSpoilerStack.clear();
+ closedSpoilerMap.clear();
+ continue;
+ }
+ }
+ return closedSpoilerMap;
+ }
+
+ private int calculateBalance(int index, SpannableStringBuilder line) {
+ final int MAX_NEW_LINE = 1;
+ int new_lines = 0;
+ int opening = 0;
+ int closing = 0;
+ for (int i = index; i < line.length(); i++) {
+ if (line.charAt(i) == '\u0020' || line.charAt(i) == '\t') {
+ continue;
+ } else if (line.charAt(i) == '>'
+ && (i + 1) < line.length()
+ && line.charAt(i + 1) == '!') {
+ opening++;
+ } else if (line.charAt(i) == '!' && (i + 1) < line.length()
+ && line.charAt(i + 1) == '<') {
+ closing++;
+ } else if (line.charAt(i) == '\n') {
+ new_lines++;
+ if (new_lines > MAX_NEW_LINE) {
+ break;
+ }
+ } else {
+ new_lines = 0;
+ continue;
+ }
+ }
+ return Math.abs(opening - closing);
+ }
+
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerSpan.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerSpan.java
new file mode 100644
index 00000000..2264449c
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SpoilerSpan.java
@@ -0,0 +1,64 @@
+package ml.docilealligator.infinityforreddit.markdown;
+
+import android.text.Layout;
+import android.text.Spannable;
+import android.text.TextPaint;
+import android.text.style.ClickableSpan;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.transition.AutoTransition;
+import androidx.transition.TransitionManager;
+
+import ml.docilealligator.infinityforreddit.customviews.SpoilerOnClickTextView;
+
+public class SpoilerSpan extends ClickableSpan {
+ final int textColor;
+ final int backgroundColor;
+ private boolean isShowing = false;
+
+ public SpoilerSpan(@NonNull int textColor, @NonNull int backgroundColor) {
+ this.textColor = textColor;
+ this.backgroundColor = backgroundColor;
+ }
+
+ @Override
+ public void onClick(@NonNull View widget) {
+ if (!(widget instanceof TextView)) {
+ return;
+ }
+
+ final TextView textView = (TextView) widget;
+ final Spannable spannable = (Spannable) textView.getText();
+
+ final int end = spannable.getSpanEnd(this);
+
+ if (end < 0) {
+ return;
+ }
+
+ final Layout layout = textView.getLayout();
+ if (layout == null) {
+ return;
+ }
+
+ if (widget instanceof SpoilerOnClickTextView) {
+ ((SpoilerOnClickTextView) textView).setSpoilerOnClick(true);
+ }
+ isShowing = !isShowing;
+ widget.invalidate();
+ }
+
+ @Override
+ public void updateDrawState(@NonNull TextPaint ds) {
+ if (isShowing) {
+ ds.bgColor = backgroundColor & 0x0D000000; //Slightly darker background color for revealed spoiler
+ super.updateDrawState(ds);
+ } else {
+ ds.bgColor = backgroundColor;
+ }
+ ds.setColor(textColor);
+ ds.setUnderlineText(false);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/SuperscriptInlineProcessor.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SuperscriptInlineProcessor.java
index 136a69d4..27f4d1e4 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/SuperscriptInlineProcessor.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/SuperscriptInlineProcessor.java
@@ -1,4 +1,4 @@
-package ml.docilealligator.infinityforreddit.utils;
+package ml.docilealligator.infinityforreddit.markdown;
import androidx.annotation.Nullable;
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 15bcefa0..f292088d 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java
@@ -65,7 +65,6 @@ public class Utils {
.replaceAll("((?<=[\\s])|^)/[rRuU]/[\\w-]+/{0,1}", "[$0](https://www.reddit.com$0)")
.replaceAll("((?<=[\\s])|^)[rRuU]/[\\w-]+/{0,1}", "[$0](https://www.reddit.com/$0)")
.replaceAll("\\^{2,}", "^")
- .replaceAll(">!(\\n?(?:[\\S\\h]+?(?:\\n?[\\S\\h]+?)?)\\n?)!<", "&gt;!$1!<") // html entity remains escaped inside an inline block
.replaceAll("(^|^ *|\\n *)#(?!($|\\s|#))", "$0 ")
.replaceAll("(^|^ *|\\n *)##(?!($|\\s|#))", "$0 ")
.replaceAll("(^|^ *|\\n *)###(?!($|\\s|#))", "$0 ")