aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/ml
diff options
context:
space:
mode:
authorDocile-Alligator <chineseperson5@gmail.com>2022-04-02 07:50:12 +0000
committerDocile-Alligator <chineseperson5@gmail.com>2022-04-02 07:50:12 +0000
commit95d792779e98b8a4180ed50f3d945865b406b127 (patch)
treeb8159cfdc8d91d4c4aca9900befaba902b498ff1 /app/src/main/java/ml
parentf0e2090db98722daec2a7dba295b8abf7d7a5514 (diff)
downloadinfinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.tar
infinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.tar.gz
infinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.tar.bz2
infinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.tar.lz
infinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.tar.xz
infinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.tar.zst
infinity-for-reddit-95d792779e98b8a4180ed50f3d945865b406b127.zip
Render full markdown in CommentsRecyclerViewAdapter.
Diffstat (limited to 'app/src/main/java/ml')
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java90
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/customviews/CustomMarkwonAdapter.java235
3 files changed, 306 insertions, 21 deletions
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 19fad657..6a0d3c53 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/CommentsRecyclerViewAdapter.java
@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.text.Spanned;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,6 +34,8 @@ import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions;
import com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar;
+import org.commonmark.ext.gfm.tables.TableBlock;
+
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.Executor;
@@ -52,6 +55,8 @@ import io.noties.markwon.inlineparser.HtmlInlineProcessor;
import io.noties.markwon.inlineparser.MarkwonInlineParserPlugin;
import io.noties.markwon.linkify.LinkifyPlugin;
import io.noties.markwon.movement.MovementMethodPlugin;
+import io.noties.markwon.recycler.table.TableEntry;
+import io.noties.markwon.recycler.table.TableEntryPlugin;
import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
import me.saket.bettermovementmethod.BetterLinkMovementMethod;
import ml.docilealligator.infinityforreddit.R;
@@ -68,6 +73,9 @@ import ml.docilealligator.infinityforreddit.comment.Comment;
import ml.docilealligator.infinityforreddit.comment.FetchComment;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.CommentIndentationView;
+import ml.docilealligator.infinityforreddit.customviews.CustomMarkwonAdapter;
+import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
+import ml.docilealligator.infinityforreddit.customviews.MarkwonLinearLayoutManager;
import ml.docilealligator.infinityforreddit.customviews.SpoilerOnClickTextView;
import ml.docilealligator.infinityforreddit.fragments.ViewPostDetailFragment;
import ml.docilealligator.infinityforreddit.markdown.SpoilerParserPlugin;
@@ -101,6 +109,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
private ArrayList<Comment> mVisibleComments;
private Locale mLocale;
private RequestManager mGlide;
+ private RecyclerView.RecycledViewPool recycledViewPool;
private String mSingleCommentId;
private boolean mIsSingleCommentThreadMode;
private boolean mVoteButtonsOnTheRight;
@@ -190,6 +199,15 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
}
@Override
+ public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
+ if (mActivity.contentTypeface != null) {
+ textView.setTypeface(mActivity.contentTypeface);
+ }
+ textView.setTextColor(mCommentTextColor);
+ textView.setHighlightColor(Color.TRANSPARENT);
+ }
+
+ @Override
public void configureConfiguration(@NonNull MarkwonConfiguration.Builder builder) {
builder.linkResolver((view, link) -> {
Intent intent = new Intent(mActivity, LinkResolverActivity.class);
@@ -207,7 +225,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
})
.usePlugin(SpoilerParserPlugin.create(mCommentTextColor, commentSpoilerBackgroundColor))
.usePlugin(StrikethroughPlugin.create())
- .usePlugin(MovementMethodPlugin.create(BetterLinkMovementMethod.newInstance().setOnLinkLongClickListener((textView, url) -> {
+ .usePlugin(MovementMethodPlugin.create(BetterLinkMovementMethod.linkify(Linkify.WEB_URLS).setOnLinkLongClickListener((textView, url) -> {
if (!activity.isDestroyed() && !activity.isFinishing()) {
UrlMenuBottomSheetFragment urlMenuBottomSheetFragment = new UrlMenuBottomSheetFragment();
Bundle bundle = new Bundle();
@@ -218,7 +236,9 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
return true;
})))
.usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS))
+ .usePlugin(TableEntryPlugin.create(mActivity))
.build();
+ recycledViewPool = new RecyclerView.RecycledViewPool();
mAccessToken = accessToken;
mAccountName = accountName;
mPost = post;
@@ -447,7 +467,8 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
Utils.setHTMLWithImageToTextView(((CommentViewHolder) holder).awardsTextView, comment.getAwards(), true);
}
- mCommentMarkwon.setMarkdown(((CommentViewHolder) holder).commentMarkdownView, comment.getCommentMarkdown());
+ ((CommentViewHolder) holder).mMarkwonAdapter.setMarkdown(mCommentMarkwon, comment.getCommentMarkdown());
+ ((CommentViewHolder) holder).mMarkwonAdapter.notifyDataSetChanged();
((CommentViewHolder) holder).scoreTextView.setText(Utils.getNVotes(mShowAbsoluteNumberOfVotes,
comment.getScore() + comment.getVoteType()));
((CommentViewHolder) holder).topScoreTextView.setText(mActivity.getString(R.string.top_score,
@@ -1143,7 +1164,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
@BindView(R.id.awards_text_view_item_comment)
TextView awardsTextView;
@BindView(R.id.comment_markdown_view_item_post_comment)
- SpoilerOnClickTextView commentMarkdownView;
+ RecyclerView commentMarkdownView;
@BindView(R.id.bottom_constraint_layout_item_post_comment)
ConstraintLayout bottomConstraintLayout;
@BindView(R.id.up_vote_button_item_post_comment)
@@ -1166,6 +1187,7 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
CommentIndentationView commentIndentationView;
@BindView(R.id.divider_item_comment)
View commentDivider;
+ CustomMarkwonAdapter mMarkwonAdapter;
CommentViewHolder(View itemView) {
super(itemView);
@@ -1225,9 +1247,6 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
scoreTextView.setTypeface(mActivity.typeface);
expandButton.setTypeface(mActivity.typeface);
}
- if (mActivity.contentTypeface != null) {
- commentMarkdownView.setTypeface(mActivity.contentTypeface);
- }
if (mShowAuthorAvatar) {
authorIconImageView.setVisibility(View.VISIBLE);
@@ -1236,10 +1255,29 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
((ConstraintLayout.LayoutParams) authorFlairTextView.getLayoutParams()).leftMargin = 0;
}
+ commentMarkdownView.setRecycledViewPool(recycledViewPool);
+ LinearLayoutManagerBugFixed linearLayoutManager = new MarkwonLinearLayoutManager(mActivity, new MarkwonLinearLayoutManager.HorizontalScrollViewScrolledListener() {
+ @Override
+ public void onScrolledLeft() {
+ ((ViewPostDetailActivity) mActivity).lockSwipeRightToGoBack();
+ }
+
+ @Override
+ public void onScrolledRight() {
+ ((ViewPostDetailActivity) mActivity).unlockSwipeRightToGoBack();
+ }
+ });
+ commentMarkdownView.setLayoutManager(linearLayoutManager);
+ mMarkwonAdapter = CustomMarkwonAdapter.builder(R.layout.adapter_default_entry, R.id.text)
+ .include(TableBlock.class, TableEntry.create(builder -> builder
+ .tableLayout(R.layout.adapter_table_block, R.id.table_layout)
+ .textLayoutIsRoot(R.layout.view_table_entry_cell)))
+ .build();
+ commentMarkdownView.setAdapter(mMarkwonAdapter);
+
itemView.setBackgroundColor(mCommentBackgroundColor);
authorTextView.setTextColor(mUsernameColor);
commentTimeTextView.setTextColor(mSecondaryTextColor);
- commentMarkdownView.setTextColor(mCommentTextColor);
authorFlairTextView.setTextColor(mAuthorFlairTextColor);
topScoreTextView.setTextColor(mSecondaryTextColor);
awardsTextView.setTextColor(mSecondaryTextColor);
@@ -1572,23 +1610,34 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
if (mCommentToolbarHideOnClick) {
View.OnLongClickListener hideToolbarOnLongClickListener = view -> hideToolbar();
itemView.setOnLongClickListener(hideToolbarOnLongClickListener);
- commentMarkdownView.setOnLongClickListener(hideToolbarOnLongClickListener);
commentTimeTextView.setOnLongClickListener(hideToolbarOnLongClickListener);
+ mMarkwonAdapter.setOnLongClickListener(v -> {
+ if (v instanceof TextView) {
+ if (((TextView) v).getSelectionStart() == -1 && ((TextView) v).getSelectionEnd() == -1) {
+ hideToolbar();
+ }
+ }
+ return true;
+ });
}
- commentMarkdownView.setOnClickListener(view -> {
- if (commentMarkdownView.isSpoilerOnClick()) {
- commentMarkdownView.setSpoilerOnClick(false);
- return;
+ mMarkwonAdapter.setOnClickListener(v -> {
+ if (v instanceof SpoilerOnClickTextView) {
+ if (((SpoilerOnClickTextView) v).isSpoilerOnClick()) {
+ ((SpoilerOnClickTextView) v).setSpoilerOnClick(false);
+ return;
+ }
}
expandComments();
});
itemView.setOnClickListener(view -> expandComments());
} else {
if (mCommentToolbarHideOnClick) {
- commentMarkdownView.setOnClickListener(view -> {
- if (commentMarkdownView.isSpoilerOnClick()) {
- commentMarkdownView.setSpoilerOnClick(false);
- return;
+ mMarkwonAdapter.setOnClickListener(view -> {
+ if (view instanceof SpoilerOnClickTextView) {
+ if (((SpoilerOnClickTextView) view).isSpoilerOnClick()) {
+ ((SpoilerOnClickTextView) view).setSpoilerOnClick(false);
+ return;
+ }
}
hideToolbar();
});
@@ -1596,9 +1645,11 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
itemView.setOnClickListener(hideToolbarOnClickListener);
commentTimeTextView.setOnClickListener(hideToolbarOnClickListener);
}
- commentMarkdownView.setOnLongClickListener(view -> {
- if (commentMarkdownView.getSelectionStart() == -1 && commentMarkdownView.getSelectionEnd() == -1) {
- expandComments();
+ mMarkwonAdapter.setOnLongClickListener(view -> {
+ if (view instanceof TextView) {
+ if (((TextView) view).getSelectionStart() == -1 && ((TextView) view).getSelectionEnd() == -1) {
+ expandComments();
+ }
}
return true;
});
@@ -1607,7 +1658,6 @@ public class CommentsRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVi
return true;
});
}
- commentMarkdownView.setHighlightColor(Color.TRANSPARENT);
}
private boolean expandComments() {
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 983c887a..ba59a7fd 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java
@@ -259,7 +259,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
@Override
public void beforeSetText(@NonNull TextView textView, @NonNull Spanned markdown) {
if (mActivity.contentTypeface != null) {
- textView.setTypeface(mActivity.typeface);
+ textView.setTypeface(mActivity.contentTypeface);
}
textView.setTextColor(markdownColor);
textView.setOnLongClickListener(view -> {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/CustomMarkwonAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/CustomMarkwonAdapter.java
new file mode 100644
index 00000000..fb10d036
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/CustomMarkwonAdapter.java
@@ -0,0 +1,235 @@
+package ml.docilealligator.infinityforreddit.customviews;
+
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+
+import org.commonmark.node.Node;
+
+import java.util.Collections;
+import java.util.List;
+
+import io.noties.markwon.Markwon;
+import io.noties.markwon.MarkwonReducer;
+import io.noties.markwon.recycler.MarkwonAdapter;
+import io.noties.markwon.recycler.SimpleEntry;
+import ml.docilealligator.infinityforreddit.R;
+
+public class CustomMarkwonAdapter extends MarkwonAdapter {
+ private final SparseArray<Entry<Node, Holder>> entries;
+ private final Entry<Node, Holder> defaultEntry;
+ private final MarkwonReducer reducer;
+
+ private LayoutInflater layoutInflater;
+
+ private Markwon markwon;
+ private List<Node> nodes;
+
+ private View.OnClickListener onClickListener;
+ private View.OnLongClickListener onLongClickListener;
+
+ @SuppressWarnings("WeakerAccess")
+ CustomMarkwonAdapter(
+ @NonNull SparseArray<Entry<Node, Holder>> entries,
+ @NonNull Entry<Node, Holder> defaultEntry,
+ @NonNull MarkwonReducer reducer) {
+ this.entries = entries;
+ this.defaultEntry = defaultEntry;
+ this.reducer = reducer;
+
+ setHasStableIds(true);
+ }
+
+ public void setOnClickListener(View.OnClickListener onClickListener) {
+ this.onClickListener = onClickListener;
+ }
+
+ public void setOnLongClickListener(View.OnLongClickListener onLongClickListener) {
+ this.onLongClickListener = onLongClickListener;
+ }
+
+ @NonNull
+ public static CustomBuilderImpl builder(
+ @LayoutRes int defaultEntryLayoutResId,
+ @IdRes int defaultEntryTextViewResId
+ ) {
+ return builder(SimpleEntry.create(defaultEntryLayoutResId, defaultEntryTextViewResId));
+ }
+
+ @NonNull
+ public static CustomBuilderImpl builder(@NonNull Entry<? extends Node, ? extends Holder> defaultEntry) {
+ //noinspection unchecked
+ return new CustomBuilderImpl((Entry<Node, Holder>) defaultEntry);
+ }
+
+ @Override
+ public void setMarkdown(@NonNull Markwon markwon, @NonNull String markdown) {
+ setParsedMarkdown(markwon, markwon.parse(markdown));
+ }
+
+ @Override
+ public void setParsedMarkdown(@NonNull Markwon markwon, @NonNull Node document) {
+ setParsedMarkdown(markwon, reducer.reduce(document));
+ }
+
+ @Override
+ public void setParsedMarkdown(@NonNull Markwon markwon, @NonNull List<Node> nodes) {
+ // clear all entries before applying
+
+ defaultEntry.clear();
+
+ for (int i = 0, size = entries.size(); i < size; i++) {
+ entries.valueAt(i).clear();
+ }
+
+ this.markwon = markwon;
+ this.nodes = nodes;
+ }
+
+ @NonNull
+ @Override
+ public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+
+ if (layoutInflater == null) {
+ layoutInflater = LayoutInflater.from(parent.getContext());
+ }
+
+ final Entry<Node, Holder> entry = getEntry(viewType);
+
+ return entry.createHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull Holder holder, int position) {
+
+ final Node node = nodes.get(position);
+ final int viewType = getNodeViewType(node.getClass());
+
+ final Entry<Node, Holder> entry = getEntry(viewType);
+
+ entry.bindHolder(markwon, holder, node);
+
+ if (holder.itemView instanceof SpoilerOnClickTextView) {
+ holder.itemView.setOnClickListener(onClickListener);
+ holder.itemView.setOnLongClickListener(onLongClickListener);
+ } else if (holder.itemView instanceof HorizontalScrollView) {
+ TableLayout tableLayout = holder.itemView.findViewById(R.id.table_layout);
+ if (tableLayout != null) {
+ for (int i = 0; i < tableLayout.getChildCount(); i++) {
+ if (tableLayout.getChildAt(i) instanceof TableRow) {
+ TableRow tableRow = ((TableRow) tableLayout.getChildAt(i));
+ for (int j = 0; j < tableRow.getChildCount(); j++) {
+ if (tableRow.getChildAt(j) instanceof TextView) {
+ tableRow.getChildAt(j).setOnClickListener(onClickListener);
+ tableRow.getChildAt(j).setOnLongClickListener(onLongClickListener);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return nodes != null
+ ? nodes.size()
+ : 0;
+ }
+
+ @Override
+ public void onViewRecycled(@NonNull Holder holder) {
+ super.onViewRecycled(holder);
+
+ final Entry<Node, Holder> entry = getEntry(holder.getItemViewType());
+ entry.onViewRecycled(holder);
+ }
+
+ @SuppressWarnings("unused")
+ @NonNull
+ public List<Node> getItems() {
+ return nodes != null
+ ? Collections.unmodifiableList(nodes)
+ : Collections.<Node>emptyList();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return getNodeViewType(nodes.get(position).getClass());
+ }
+
+ @Override
+ public long getItemId(int position) {
+ final Node node = nodes.get(position);
+ final int type = getNodeViewType(node.getClass());
+ final Entry<Node, Holder> entry = getEntry(type);
+ return entry.id(node);
+ }
+
+ @Override
+ public int getNodeViewType(@NonNull Class<? extends Node> node) {
+ // if has registered -> then return it, else 0
+ final int hash = node.hashCode();
+ if (entries.indexOfKey(hash) > -1) {
+ return hash;
+ }
+ return 0;
+ }
+
+ @NonNull
+ private Entry<Node, Holder> getEntry(int viewType) {
+ return viewType == 0
+ ? defaultEntry
+ : entries.get(viewType);
+ }
+
+ public static class CustomBuilderImpl implements Builder {
+
+ private final SparseArray<Entry<Node, Holder>> entries = new SparseArray<>(3);
+
+ private final Entry<Node, Holder> defaultEntry;
+
+ private MarkwonReducer reducer;
+
+ CustomBuilderImpl(@NonNull Entry<Node, Holder> defaultEntry) {
+ this.defaultEntry = defaultEntry;
+ }
+
+ @NonNull
+ @Override
+ public <N extends Node> CustomBuilderImpl include(
+ @NonNull Class<N> node,
+ @NonNull Entry<? super N, ? extends Holder> entry) {
+ //noinspection unchecked
+ entries.append(node.hashCode(), (Entry<Node, Holder>) entry);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public CustomBuilderImpl reducer(@NonNull MarkwonReducer reducer) {
+ this.reducer = reducer;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public CustomMarkwonAdapter build() {
+
+ if (reducer == null) {
+ reducer = MarkwonReducer.directChildren();
+ }
+
+ return new CustomMarkwonAdapter(entries, defaultEntry, reducer);
+ }
+ }
+}