aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/CommentActivity.java5
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/FullMarkdownActivity.java5
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/activities/WikiActivity.java5
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsListingRecyclerViewAdapter.java7
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java7
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java7
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java6
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/comment/ParseComment.java53
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java5
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/Emote.java34
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteCloseBracketInlineProcessor.java156
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteInlineProcessor.java32
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmotePlugin.java230
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteSpanFactory.java29
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/ImageAndGifBlockParser.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/markdown/MarkdownUtils.java6
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/utils/Utils.java2
17 files changed, 559 insertions, 32 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 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<Comment
private BaseActivity mActivity;
private Retrofit mOauthRetrofit;
private Locale mLocale;
+ private EmoteCloseBracketInlineProcessor mEmoteCloseBracketInlineProcessor;
private ImageAndGifPlugin mImageAndGifPlugin;
private Markwon mMarkwon;
private ImageAndGifEntry mImageAndGifEntry;
@@ -177,9 +179,11 @@ public class CommentsListingRecyclerViewAdapter extends PagedListAdapter<Comment
}
return true;
};
+ mEmoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor();
mImageAndGifPlugin = new ImageAndGifPlugin();
mMarkwon = MarkdownUtils.createFullRedditMarkwon(mActivity,
- miscPlugin, mImageAndGifPlugin, mCommentColor, commentSpoilerBackgroundColor, onLinkLongClickListener);
+ miscPlugin, mEmoteCloseBracketInlineProcessor, mImageAndGifPlugin, mCommentColor,
+ commentSpoilerBackgroundColor, onLinkLongClickListener);
mImageAndGifEntry = new ImageAndGifEntry(activity, Glide.with(activity), new ImageAndGifEntry.OnItemClickListener() {
@Override
public void onItemClick(MediaMetadata mediaMetadata) {
@@ -241,6 +245,7 @@ public class CommentsListingRecyclerViewAdapter extends PagedListAdapter<Comment
Utils.setHTMLWithImageToTextView(((CommentBaseViewHolder) holder).awardsTextView, comment.getAwards(), true);
}
+ mEmoteCloseBracketInlineProcessor.setMediaMetadataMap(comment.getMediaMetadataMap());
mImageAndGifPlugin.setMediaMetadataMap(comment.getMediaMetadataMap());
((CommentBaseViewHolder) holder).markwonAdapter.setMarkdown(mMarkwon, comment.getCommentMarkdown());
// noinspection NotifyDataSetChanged
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 9702bfb8..5202b4a1 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
@@ -69,6 +69,7 @@ import ml.docilealligator.infinityforreddit.customviews.SwipeLockInterface;
import ml.docilealligator.infinityforreddit.customviews.SwipeLockLinearLayoutManager;
import ml.docilealligator.infinityforreddit.databinding.ItemCommentBinding;
import ml.docilealligator.infinityforreddit.fragments.ViewPostDetailFragment;
+import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin;
import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils;
@@ -97,6 +98,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
private Executor mExecutor;
private Retrofit mRetrofit;
private Retrofit mOauthRetrofit;
+ private EmoteCloseBracketInlineProcessor mEmoteCloseBracketInlineProcessor;
private ImageAndGifPlugin mImageAndGifPlugin;
private Markwon mCommentMarkwon;
private ImageAndGifEntry mImageAndGifEntry;
@@ -211,9 +213,11 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
}
return true;
};
+ mEmoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor();
mImageAndGifPlugin = new ImageAndGifPlugin();
mCommentMarkwon = MarkdownUtils.createFullRedditMarkwon(mActivity,
- miscPlugin, mImageAndGifPlugin, mCommentTextColor, commentSpoilerBackgroundColor, onLinkLongClickListener);
+ miscPlugin, mEmoteCloseBracketInlineProcessor, mImageAndGifPlugin, mCommentTextColor,
+ commentSpoilerBackgroundColor, onLinkLongClickListener);
mImageAndGifEntry = new ImageAndGifEntry(activity, mGlide, new ImageAndGifEntry.OnItemClickListener() {
@Override
public void onItemClick(MediaMetadata mediaMetadata) {
@@ -466,6 +470,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
Utils.setHTMLWithImageToTextView(((CommentBaseViewHolder) holder).awardsTextView, comment.getAwards(), true);
}
+ mEmoteCloseBracketInlineProcessor.setMediaMetadataMap(comment.getMediaMetadataMap());
mImageAndGifPlugin.setMediaMetadataMap(comment.getMediaMetadataMap());
((CommentBaseViewHolder) holder).mMarkwonAdapter.setMarkdown(mCommentMarkwon, comment.getCommentMarkdown());
// noinspection NotifyDataSetChanged
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 11b04252..f50c62c3 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
@@ -103,6 +103,7 @@ import ml.docilealligator.infinityforreddit.databinding.ItemPostDetailVideoAndGi
import ml.docilealligator.infinityforreddit.databinding.ItemPostDetailVideoAutoplayBinding;
import ml.docilealligator.infinityforreddit.databinding.ItemPostDetailVideoAutoplayLegacyControllerBinding;
import ml.docilealligator.infinityforreddit.fragments.ViewPostDetailFragment;
+import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin;
import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils;
@@ -144,6 +145,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
private SharedPreferences mCurrentAccountSharedPreferences;
private RequestManager mGlide;
private SaveMemoryCenterInisdeDownsampleStrategy mSaveMemoryCenterInsideDownsampleStrategy;
+ private EmoteCloseBracketInlineProcessor mEmoteCloseBracketInlineProcessor;
private ImageAndGifPlugin mImageAndGifPlugin;
private Markwon mPostDetailMarkwon;
private ImageAndGifEntry mImageAndGifEntry;
@@ -383,9 +385,11 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
}
return true;
};
+ mEmoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor();
mImageAndGifPlugin = new ImageAndGifPlugin();
mPostDetailMarkwon = MarkdownUtils.createFullRedditMarkwon(mActivity,
- miscPlugin, mImageAndGifPlugin, markdownColor, postSpoilerBackgroundColor, onLinkLongClickListener);
+ miscPlugin, mEmoteCloseBracketInlineProcessor, mImageAndGifPlugin, markdownColor,
+ postSpoilerBackgroundColor, onLinkLongClickListener);
mImageAndGifEntry = new ImageAndGifEntry(activity,
mGlide, mDataSavingMode,
(post.isNSFW() && mNeedBlurNsfw && !(mDoNotBlurNsfwInNsfwSubreddits && mFragment != null && mFragment.getIsNsfwSubreddit())) || (mPost.isSpoiler() && mNeedBlurSpoiler),
@@ -666,6 +670,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
if (mPost.getSelfText() != null && !mPost.getSelfText().equals("")) {
((PostDetailBaseViewHolder) holder).contentMarkdownView.setVisibility(View.VISIBLE);
((PostDetailBaseViewHolder) holder).contentMarkdownView.setAdapter(mMarkwonAdapter);
+ mEmoteCloseBracketInlineProcessor.setMediaMetadataMap(mPost.getMediaMetadataMap());
mImageAndGifPlugin.setMediaMetadataMap(mPost.getMediaMetadataMap());
mMarkwonAdapter.setMarkdown(mPostDetailMarkwon, mPost.getSelfText());
// noinspection NotifyDataSetChanged
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 02b88c0f..40c4e19f 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/RulesRecyclerViewAdapter.java
@@ -35,12 +35,14 @@ import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.SwipeLockInterface;
import ml.docilealligator.infinityforreddit.customviews.SwipeLockLinearLayoutManager;
import ml.docilealligator.infinityforreddit.customviews.slidr.widget.SliderPanel;
+import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin;
import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils;
public class RulesRecyclerViewAdapter extends RecyclerView.Adapter<RulesRecyclerViewAdapter.RuleViewHolder> {
private BaseActivity activity;
+ private EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor;
private ImageAndGifPlugin imageAndGifPlugin;
private Markwon markwon;
@Nullable
@@ -87,9 +89,11 @@ public class RulesRecyclerViewAdapter extends RecyclerView.Adapter<RulesRecycler
}
return true;
};
+ emoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor();
imageAndGifPlugin = new ImageAndGifPlugin();
markwon = MarkdownUtils.createFullRedditMarkwon(activity,
- miscPlugin, imageAndGifPlugin, mPrimaryTextColor, spoilerBackgroundColor, onLinkLongClickListener);
+ miscPlugin, emoteCloseBracketInlineProcessor, imageAndGifPlugin, mPrimaryTextColor,
+ spoilerBackgroundColor, onLinkLongClickListener);
}
@NonNull
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/comment/ParseComment.java b/app/src/main/java/ml/docilealligator/infinityforreddit/comment/ParseComment.java
index 4ba92570..d08bebd1 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/comment/ParseComment.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/comment/ParseComment.java
@@ -339,31 +339,40 @@ public class ParseComment {
String k = it.next();
JSONObject media = mediaMetadataJSON.getJSONObject(k);
String e = media.getString(JSONUtils.E_KEY);
- JSONArray downscales = media.getJSONArray(JSONUtils.P_KEY);
- JSONObject downscaledItemJSON;
- if (downscales.length() <= 3) {
- downscaledItemJSON = downscales.getJSONObject(downscales.length() - 1);
- } else {
- downscaledItemJSON = downscales.getJSONObject(3);
- }
- MediaMetadata.MediaItem downscaledItem = new MediaMetadata.MediaItem(downscaledItemJSON.getInt(JSONUtils.X_KEY),
- downscaledItemJSON.getInt(JSONUtils.Y_KEY), downscaledItemJSON.getString(JSONUtils.U_KEY));
+
+ JSONObject originalItemJSON = media.getJSONObject(JSONUtils.S_KEY);
+ MediaMetadata.MediaItem originalItem;
if (e.equalsIgnoreCase("Image")) {
- JSONObject originalItemJSON = media.getJSONObject(JSONUtils.S_KEY);
- MediaMetadata.MediaItem originalItem = new MediaMetadata.MediaItem(originalItemJSON.getInt(JSONUtils.X_KEY),
+ originalItem = new MediaMetadata.MediaItem(originalItemJSON.getInt(JSONUtils.X_KEY),
originalItemJSON.getInt(JSONUtils.Y_KEY), originalItemJSON.getString(JSONUtils.U_KEY));
-
- String id = media.getString(JSONUtils.ID_KEY);
- mediaMetadataMap.put(id, new MediaMetadata(id, e, originalItem, downscaledItem));
- } else if (e.equalsIgnoreCase("AnimatedImage")) {
- JSONObject originalItemJSON = media.getJSONObject(JSONUtils.S_KEY);
- MediaMetadata.MediaItem originalItem = new MediaMetadata.MediaItem(originalItemJSON.getInt(JSONUtils.X_KEY),
- originalItemJSON.getInt(JSONUtils.Y_KEY), originalItemJSON.getString(JSONUtils.GIF_KEY),
- originalItemJSON.getString(JSONUtils.MP4_KEY));
-
- String id = media.getString(JSONUtils.ID_KEY);
- mediaMetadataMap.put(id, new MediaMetadata(id, e, originalItem, downscaledItem));
+ } else {
+ if (originalItemJSON.has(JSONUtils.MP4_KEY)) {
+ originalItem = new MediaMetadata.MediaItem(originalItemJSON.getInt(JSONUtils.X_KEY),
+ originalItemJSON.getInt(JSONUtils.Y_KEY), originalItemJSON.getString(JSONUtils.GIF_KEY),
+ originalItemJSON.getString(JSONUtils.MP4_KEY));
+ } else {
+ originalItem = new MediaMetadata.MediaItem(originalItemJSON.getInt(JSONUtils.X_KEY),
+ originalItemJSON.getInt(JSONUtils.Y_KEY), originalItemJSON.getString(JSONUtils.GIF_KEY));
+ }
+ }
+ String id = media.getString(JSONUtils.ID_KEY);
+
+ MediaMetadata.MediaItem downscaledItem;
+ if (media.has(JSONUtils.P_KEY)) {
+ JSONArray downscales = media.getJSONArray(JSONUtils.P_KEY);
+ JSONObject downscaledItemJSON;
+ if (downscales.length() <= 3) {
+ downscaledItemJSON = downscales.getJSONObject(downscales.length() - 1);
+ } else {
+ downscaledItemJSON = downscales.getJSONObject(3);
+ }
+ downscaledItem = new MediaMetadata.MediaItem(downscaledItemJSON.getInt(JSONUtils.X_KEY),
+ downscaledItemJSON.getInt(JSONUtils.Y_KEY), downscaledItemJSON.getString(JSONUtils.U_KEY));
+ } else {
+ downscaledItem = originalItem;
}
+
+ mediaMetadataMap.put(id, new MediaMetadata(id, e, originalItem, downscaledItem));
} catch (JSONException e) {
e.printStackTrace();
}
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 3b42ee22..821e6926 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/fragments/SidebarFragment.java
@@ -45,6 +45,7 @@ import ml.docilealligator.infinityforreddit.bottomsheetfragments.CopyTextBottomS
import ml.docilealligator.infinityforreddit.bottomsheetfragments.UrlMenuBottomSheetFragment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
+import ml.docilealligator.infinityforreddit.markdown.EmoteCloseBracketInlineProcessor;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifEntry;
import ml.docilealligator.infinityforreddit.markdown.ImageAndGifPlugin;
import ml.docilealligator.infinityforreddit.markdown.MarkdownUtils;
@@ -146,9 +147,11 @@ public class SidebarFragment extends Fragment {
urlMenuBottomSheetFragment.show(getChildFragmentManager(), null);
return true;
};
+ EmoteCloseBracketInlineProcessor emoteCloseBracketInlineProcessor = new EmoteCloseBracketInlineProcessor();
ImageAndGifPlugin imageAndGifPlugin = new ImageAndGifPlugin();
Markwon markwon = MarkdownUtils.createFullRedditMarkwon(activity,
- miscPlugin, imageAndGifPlugin, markdownColor, spoilerBackgroundColor, onLinkLongClickListener);
+ miscPlugin, emoteCloseBracketInlineProcessor, imageAndGifPlugin, markdownColor,
+ spoilerBackgroundColor, onLinkLongClickListener);
MarkwonAdapter markwonAdapter = MarkdownUtils.createTablesAdapter(new ImageAndGifEntry(activity,
Glide.with(this),
new ImageAndGifEntry.OnItemClickListener() {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/Emote.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/Emote.java
new file mode 100644
index 00000000..90517682
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/Emote.java
@@ -0,0 +1,34 @@
+package ml.docilealligator.infinityforreddit.markdown;
+
+import org.commonmark.node.CustomNode;
+import org.commonmark.node.Visitor;
+
+import ml.docilealligator.infinityforreddit.MediaMetadata;
+
+public class Emote extends CustomNode {
+ private final MediaMetadata mediaMetadata;
+ private final String title;
+
+ public Emote(MediaMetadata mediaMetadata, String title) {
+ this.mediaMetadata = mediaMetadata;
+ this.title = title;
+ }
+
+ @Override
+ public void accept(Visitor visitor) {
+ visitor.visit(this);
+ }
+
+ public MediaMetadata getMediaMetadata() {
+ return mediaMetadata;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ protected String toStringAttributes() {
+ return "destination=" + mediaMetadata.original.url + ", title=" + title;
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteCloseBracketInlineProcessor.java b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteCloseBracketInlineProcessor.java
new file mode 100644
index 00000000..04856f3b
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/markdown/EmoteCloseBracketInlineProcessor.java
@@ -0,0 +1,156 @@
+package ml.docilealligator.infinityforreddit.markdown;
+
+import static io.noties.markwon.inlineparser.InlineParserUtils.mergeChildTextNodes;
+
+import org.commonmark.internal.Bracket;
+import org.commonmark.internal.util.Escaping;
+import org.commonmark.node.Link;
+import org.commonmark.node.LinkReferenceDefinition;
+import org.commonmark.node.Node;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import io.noties.markwon.inlineparser.InlineProcessor;
+import ml.docilealligator.infinityforreddit.MediaMetadata;
+
+public class EmoteCloseBracketInlineProcessor extends InlineProcessor {
+ private static final Pattern WHITESPACE = Pattern.compile("\\s+");
+
+ private Map<String, MediaMetadata> 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<String, MediaMetadata> 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<Drawable> 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<Drawable> 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<Emote>() {
+ @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<String, MediaMetadata> mediaMetadataMap) {
+
+ }
+
+ private static class GlideAsyncDrawableLoader extends AsyncDrawableLoader {
+
+ private final GlideStore glideStore;
+ private final Map<AsyncDrawable, Target<?>> cache = new HashMap<>(2);
+
+ GlideAsyncDrawableLoader(@NonNull GlideStore glideStore) {
+ this.glideStore = glideStore;
+ }
+
+ @Override
+ public void load(@NonNull AsyncDrawable drawable) {
+ final Target<Drawable> 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<Drawable> {
+
+ private final AsyncDrawable drawable;
+
+ AsyncDrawableTarget(@NonNull AsyncDrawable drawable) {
+ this.drawable = drawable;
+ }
+
+ @Override
+ public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> 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<String, MediaMetadata> 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) {