From c4690a6e38004fa0442e6c15b9637ee209e9c2dd Mon Sep 17 00:00:00 2001 From: Alex Ning Date: Tue, 25 Jun 2019 09:21:44 +0800 Subject: [PATCH] Reimplement ViewPostActivity using one recyclerview to display a post and its comments instead of using a recyclerview inside NestedScrollView to prevent onBindViewHolder gets called for all the comments data at once and thus consumes more memory and freezes after the comments are loaded. --- .../CommentRecyclerViewAdapter.java | 644 ++++++++++++++++-- .../infinityforreddit/FetchComment.java | 2 +- .../LoadSubredditIconAsyncTask.java | 4 +- .../infinityforreddit/RedditAPI.java | 2 +- .../ViewPostDetailActivity.java | 499 ++------------ ...dicator.png => no_comment_placeholder.png} | Bin .../res/layout/activity_view_post_detail.xml | 335 +-------- .../main/res/layout/item_load_comments.xml | 19 + .../item_load_comments_failed_placeholder.xml | 8 + .../layout/item_no_comment_placeholder.xml | 8 + app/src/main/res/layout/item_post_detail.xml | 252 +++++++ 11 files changed, 927 insertions(+), 846 deletions(-) rename app/src/main/res/drawable/{no_comment_indicator.png => no_comment_placeholder.png} (100%) create mode 100644 app/src/main/res/layout/item_load_comments.xml create mode 100644 app/src/main/res/layout/item_load_comments_failed_placeholder.xml create mode 100644 app/src/main/res/layout/item_no_comment_placeholder.xml create mode 100644 app/src/main/res/layout/item_post_detail.xml diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentRecyclerViewAdapter.java index d2b34ecd..d987a6b7 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentRecyclerViewAdapter.java @@ -3,55 +3,113 @@ package ml.docilealligator.infinityforreddit; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.browser.customtabs.CustomTabsIntent; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.Target; +import com.google.android.material.chip.Chip; +import com.santalu.aspectratioimageview.AspectRatioImageView; + import java.util.ArrayList; import java.util.Locale; +import CustomView.AspectRatioGifImageView; +import SubredditDatabase.SubredditRoomDatabase; import butterknife.BindView; import butterknife.ButterKnife; +import jp.wasabeef.glide.transformations.BlurTransformation; +import jp.wasabeef.glide.transformations.RoundedCornersTransformation; import retrofit2.Retrofit; import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.view.MarkwonView; class CommentRecyclerViewAdapter extends RecyclerView.Adapter { - private static final int VIEW_TYPE_COMMENT = 0; - private static final int VIEW_TYPE_LOAD_MORE_COMMENT = 1; + private static final int VIEW_TYPE_POST_DETAIL = 0; + private static final int VIEW_TYPE_FIRST_LOADING = 1; + private static final int VIEW_TYPE_FIRST_LOADING_FAILED = 2; + private static final int VIEW_TYPE_NO_COMMENT_PLACEHOLDER = 3; + private static final int VIEW_TYPE_COMMENT = 4; + private static final int VIEW_TYPE_LOAD_MORE_COMMENT = 5; private Activity mActivity; private Retrofit mRetrofit; private Retrofit mOauthRetrofit; + private RequestManager mGlide; private SharedPreferences mSharedPreferences; + private Post mPost; + private ArrayList mVisibleComments; private String mSubredditNamePrefixed; private Locale mLocale; + private UpdatePostInPostFragmentCallback mUpdatePostInPostFragmentCallback; + private LoadSubredditIconAsyncTask mLoadSubredditIconAsyncTask; + private boolean isInitiallyLoading; + private boolean isInitiallyLoadingFailed; - private ArrayList mVisibleComments; + interface UpdatePostInPostFragmentCallback { + void updatePost(Post post); + } - CommentRecyclerViewAdapter(Activity activity, Retrofit retrofit, Retrofit oauthRetrofit, - SharedPreferences sharedPreferences, ArrayList expandedComments, - String subredditNamePrefixed, Locale locale) { + CommentRecyclerViewAdapter(Activity activity, Retrofit retrofit, Retrofit oauthRetrofit, RequestManager glide, + SharedPreferences sharedPreferences, Post post, String subredditNamePrefixed, + Locale locale, LoadSubredditIconAsyncTask loadSubredditIconAsyncTask, + UpdatePostInPostFragmentCallback updatePostInPostFragmentCallback) { mActivity = activity; mRetrofit = retrofit; mOauthRetrofit = oauthRetrofit; + mGlide = glide; mSharedPreferences = sharedPreferences; + mPost = post; + mVisibleComments = new ArrayList<>(); mSubredditNamePrefixed = subredditNamePrefixed; mLocale = locale; - mVisibleComments = expandedComments; + mLoadSubredditIconAsyncTask = loadSubredditIconAsyncTask; + mUpdatePostInPostFragmentCallback = updatePostInPostFragmentCallback; + isInitiallyLoading = true; + isInitiallyLoadingFailed = false; } @Override public int getItemViewType(int position) { - CommentData comment = mVisibleComments.get(position); + if(position == 0) { + return VIEW_TYPE_POST_DETAIL; + } + + if(mVisibleComments.size() == 0) { + if(position == 1) { + if(isInitiallyLoading) { + return VIEW_TYPE_FIRST_LOADING; + } else if(isInitiallyLoadingFailed) { + return VIEW_TYPE_FIRST_LOADING_FAILED; + } else { + return VIEW_TYPE_NO_COMMENT_PLACEHOLDER; + } + } + } + + CommentData comment = mVisibleComments.get(position - 1); if(!comment.isPlaceHolder()) { return VIEW_TYPE_COMMENT; } else { @@ -62,7 +120,15 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter { + if(!iconImageUrl.equals("")) { + mGlide.load(iconImageUrl) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) + .error(mGlide.load(R.drawable.subreddit_default_icon) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))) + .into(((PostDetailViewHolder) holder).mSubredditIconGifImageView); + } else { + mGlide.load(R.drawable.subreddit_default_icon) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) + .into(((PostDetailViewHolder) holder).mSubredditIconGifImageView); + } + + mPost.setSubredditIconUrl(iconImageUrl); + }); + } + + mLoadSubredditIconAsyncTask.execute(); + } else if(!mPost.getSubredditIconUrl().equals("")) { + mGlide.load(mPost.getSubredditIconUrl()) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) + .error(mGlide.load(R.drawable.subreddit_default_icon) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))) + .into(((PostDetailViewHolder) holder).mSubredditIconGifImageView); + } else { + mGlide.load(R.drawable.subreddit_default_icon) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) + .into(((PostDetailViewHolder) holder).mSubredditIconGifImageView); + } + + switch (mPost.getVoteType()) { + case 1: + //Upvote + ((PostDetailViewHolder) holder).mUpvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.backgroundColorPrimaryDark), PorterDuff.Mode.SRC_IN); + break; + case -1: + //Downvote + ((PostDetailViewHolder) holder).mDownvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.minusButtonColor), PorterDuff.Mode.SRC_IN); + break; + case 0: + ((PostDetailViewHolder) holder).mUpvoteButton.clearColorFilter(); + ((PostDetailViewHolder) holder).mDownvoteButton.clearColorFilter(); + } + + if(mPost.getPostType() != Post.TEXT_TYPE && mPost.getPostType() != Post.NO_PREVIEW_LINK_TYPE) { + ((PostDetailViewHolder) holder).mRelativeLayout.setVisibility(View.VISIBLE); + ((PostDetailViewHolder) holder).mImageView.setVisibility(View.VISIBLE); + ((PostDetailViewHolder) holder).mImageView.setRatio((float) mPost.getPreviewHeight() / (float) mPost.getPreviewWidth()); + loadImage((PostDetailViewHolder) holder); + } else { + ((PostDetailViewHolder) holder).mRelativeLayout.setVisibility(View.GONE); + ((PostDetailViewHolder) holder).mImageView.setVisibility(View.GONE); + } + + if(mPost.isCrosspost()) { + ((PostDetailViewHolder) holder).mCrosspostImageView.setVisibility(View.VISIBLE); + } + + ((PostDetailViewHolder) holder).mSubredditTextView.setText(mPost.getSubredditNamePrefixed()); + + ((PostDetailViewHolder) holder).mPostTimeTextView.setText(mPost.getPostTime()); + + if(mPost.getGilded() > 0) { + ((PostDetailViewHolder) holder).mGildedImageView.setVisibility(View.VISIBLE); + mGlide.load(R.drawable.gold).into(((PostDetailViewHolder) holder).mGildedImageView); + ((PostDetailViewHolder) holder).mGildedNumberTextView.setVisibility(View.VISIBLE); + String gildedNumber = mActivity.getResources().getString(R.string.gilded, mPost.getGilded()); + ((PostDetailViewHolder) holder).mGildedNumberTextView.setText(gildedNumber); + } + + if(mPost.isNSFW()) { + ((PostDetailViewHolder) holder).mNSFWChip.setVisibility(View.VISIBLE); + } else { + ((PostDetailViewHolder) holder).mNSFWChip.setVisibility(View.GONE); + } + + String scoreWithVote = Integer.toString(mPost.getScore() + mPost.getVoteType()); + ((PostDetailViewHolder) holder).mScoreTextView.setText(scoreWithVote); + + switch (mPost.getPostType()) { + case Post.IMAGE_TYPE: + ((PostDetailViewHolder) holder).mTypeChip.setText("IMAGE"); + + ((PostDetailViewHolder) holder).mImageView.setOnClickListener(view -> { + Intent intent = new Intent(mActivity, ViewImageActivity.class); + intent.putExtra(ViewImageActivity.IMAGE_URL_KEY, mPost.getUrl()); + intent.putExtra(ViewImageActivity.TITLE_KEY, mPost.getTitle()); + intent.putExtra(ViewImageActivity.FILE_NAME_KEY, mPost.getSubredditNamePrefixed().substring(2) + + "-" + mPost.getId().substring(3)); + mActivity.startActivity(intent); + }); + break; + case Post.LINK_TYPE: + ((PostDetailViewHolder) holder).mTypeChip.setText("LINK"); + + ((PostDetailViewHolder) holder).linkTextView.setVisibility(View.VISIBLE); + String domain = Uri.parse(mPost.getUrl()).getHost(); + ((PostDetailViewHolder) holder).linkTextView.setText(domain); + + ((PostDetailViewHolder) holder).mImageView.setOnClickListener(view -> { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + // add share action to menu list + builder.addDefaultShareMenuItem(); + builder.setToolbarColor(mActivity.getResources().getColor(R.color.colorPrimary)); + CustomTabsIntent customTabsIntent = builder.build(); + customTabsIntent.launchUrl(mActivity, Uri.parse(mPost.getUrl())); + }); + break; + case Post.GIF_VIDEO_TYPE: + ((PostDetailViewHolder) holder).mTypeChip.setText("GIF"); + + final Uri gifVideoUri = Uri.parse(mPost.getVideoUrl()); + ((PostDetailViewHolder) holder).mImageView.setOnClickListener(view -> { + Intent intent = new Intent(mActivity, ViewVideoActivity.class); + intent.setData(gifVideoUri); + intent.putExtra(ViewVideoActivity.TITLE_KEY, mPost.getTitle()); + intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, mPost.isDashVideo()); + intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, mPost.isDownloadableGifOrVideo()); + if(mPost.isDownloadableGifOrVideo()) { + intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, mPost.getGifOrVideoDownloadUrl()); + intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, mPost.getSubredditNamePrefixed()); + intent.putExtra(ViewVideoActivity.ID_KEY, mPost.getId()); + } + mActivity.startActivity(intent); + }); + break; + case Post.VIDEO_TYPE: + ((PostDetailViewHolder) holder).mTypeChip.setText("VIDEO"); + + final Uri videoUri = Uri.parse(mPost.getVideoUrl()); + ((PostDetailViewHolder) holder).mImageView.setOnClickListener(view -> { + Intent intent = new Intent(mActivity, ViewVideoActivity.class); + intent.setData(videoUri); + intent.putExtra(ViewVideoActivity.TITLE_KEY, mPost.getTitle()); + intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, mPost.isDashVideo()); + intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, mPost.isDownloadableGifOrVideo()); + if(mPost.isDownloadableGifOrVideo()) { + intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, mPost.getGifOrVideoDownloadUrl()); + intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, mPost.getSubredditNamePrefixed()); + intent.putExtra(ViewVideoActivity.ID_KEY, mPost.getId()); + } + mActivity.startActivity(intent); + }); + break; + case Post.NO_PREVIEW_LINK_TYPE: + ((PostDetailViewHolder) holder).mTypeChip.setText("LINK"); + + ((PostDetailViewHolder) holder).linkTextView.setVisibility(View.VISIBLE); + String noPreviewLinkDomain = Uri.parse(mPost.getUrl()).getHost(); + ((PostDetailViewHolder) holder).linkTextView.setText(noPreviewLinkDomain); + + if(!mPost.getSelfText().equals("")) { + ((PostDetailViewHolder) holder).mContentMarkdownView.setVisibility(View.VISIBLE); + ((PostDetailViewHolder) holder).mContentMarkdownView.setMarkdown(getCustomSpannableConfiguration(), mPost.getSelfText()); + } + ((PostDetailViewHolder) holder).mNoPreviewLinkImageView.setVisibility(View.VISIBLE); + ((PostDetailViewHolder) holder).mNoPreviewLinkImageView.setOnClickListener(view -> { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + // add share action to menu list + builder.addDefaultShareMenuItem(); + builder.setToolbarColor(mActivity.getResources().getColor(R.color.colorPrimary)); + CustomTabsIntent customTabsIntent = builder.build(); + customTabsIntent.launchUrl(mActivity, Uri.parse(mPost.getUrl())); + }); + break; + case Post.TEXT_TYPE: + ((PostDetailViewHolder) holder).mTypeChip.setText("TEXT"); + + if(!mPost.getSelfText().equals("")) { + ((PostDetailViewHolder) holder).mContentMarkdownView.setVisibility(View.VISIBLE); + ((PostDetailViewHolder) holder).mContentMarkdownView.setMarkdown(getCustomSpannableConfiguration(), mPost.getSelfText()); + } + break; + } + } else if(holder.getItemViewType() == VIEW_TYPE_COMMENT) { + CommentData commentItem; + if(isInitiallyLoading || isInitiallyLoadingFailed) { + commentItem = mVisibleComments.get(holder.getAdapterPosition() - 2); + } else { + commentItem = mVisibleComments.get(holder.getAdapterPosition() - 1); + } String authorPrefixed = "u/" + commentItem.getAuthor(); ((CommentViewHolder) holder).authorTextView.setText(authorPrefixed); @@ -122,11 +376,17 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + holder.mLoadImageProgressBar.setVisibility(View.GONE); + holder.mLoadImageErrorTextView.setVisibility(View.VISIBLE); + holder.mLoadImageErrorTextView.setOnClickListener(view -> { + holder.mLoadImageProgressBar.setVisibility(View.VISIBLE); + holder.mLoadImageErrorTextView.setVisibility(View.GONE); + loadImage(holder); + }); + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + holder.mLoadWrapper.setVisibility(View.GONE); + return false; + } + }); + + if(mPost.isNSFW()) { + imageRequestBuilder.apply(RequestOptions.bitmapTransform(new BlurTransformation(50, 2))) + .into(holder.mImageView); + } else { + imageRequestBuilder.into(holder.mImageView); + } + } + + private SpannableConfiguration getCustomSpannableConfiguration() { + return SpannableConfiguration.builder(mActivity).linkResolver((view, link) -> { + if(link.startsWith("/u/") || link.startsWith("u/")) { + Intent intent = new Intent(mActivity, ViewUserDetailActivity.class); + intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, link.substring(3)); + mActivity.startActivity(intent); + } else if(link.startsWith("/r/") || link.startsWith("r/")) { + Intent intent = new Intent(mActivity, ViewSubredditDetailActivity.class); + intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, link.substring(3)); + mActivity.startActivity(intent); + } else { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + // add share action to menu list + builder.addDefaultShareMenuItem(); + builder.setToolbarColor(mActivity.getResources().getColor(R.color.colorPrimary)); + CustomTabsIntent customTabsIntent = builder.build(); + customTabsIntent.launchUrl(mActivity, Uri.parse(link)); + } + }).build(); + } + + void updatePost(Post post) { + mPost = post; + notifyItemChanged(0); + } + private int getParentPosition(int position) { int childDepth = mVisibleComments.get(position).getDepth(); for(int i = position; i >= 0; i--) { @@ -176,9 +493,19 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter comments) { + if(mVisibleComments.size() == 0) { + isInitiallyLoading = false; + isInitiallyLoadingFailed = false; + if(comments.size() == 0) { + notifyItemChanged(1); + } else { + notifyItemRemoved(1); + } + } + int sizeBefore = mVisibleComments.size(); mVisibleComments.addAll(comments); - notifyItemRangeInserted(sizeBefore, comments.size()); + notifyItemRangeInserted(sizeBefore + 1, comments.size()); } void addComment(CommentData comment) { @@ -207,9 +534,36 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter { + Intent intent = new Intent(mActivity, ViewSubredditDetailActivity.class); + intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, + mPost.getSubredditNamePrefixed().substring(2)); + mActivity.startActivity(intent); + }); + + mShareButton.setOnClickListener(view -> { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + String extraText = mPost.getTitle() + "\n" + mPost.getPermalink(); + intent.putExtra(Intent.EXTRA_TEXT, extraText); + mActivity.startActivity(Intent.createChooser(intent, "Share")); + }); + + mUpvoteButton.setOnClickListener(view -> { + ColorFilter previousUpvoteButtonColorFilter = mUpvoteButton.getColorFilter(); + ColorFilter previousDownvoteButtonColorFilter = mDownvoteButton.getColorFilter(); + int previousVoteType = mPost.getVoteType(); + String newVoteType; + + mDownvoteButton.clearColorFilter(); + + if(previousUpvoteButtonColorFilter == null) { + //Not upvoted before + mPost.setVoteType(1); + newVoteType = RedditUtils.DIR_UPVOTE; + mUpvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.backgroundColorPrimaryDark), android.graphics.PorterDuff.Mode.SRC_IN); + } else { + //Upvoted before + mPost.setVoteType(0); + newVoteType = RedditUtils.DIR_UNVOTE; + mUpvoteButton.clearColorFilter(); + } + + mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); + + mUpdatePostInPostFragmentCallback.updatePost(mPost); + + VoteThing.voteThing(mOauthRetrofit, mSharedPreferences, new VoteThing.VoteThingWithoutPositionListener() { + @Override + public void onVoteThingSuccess() { + if(newVoteType.equals(RedditUtils.DIR_UPVOTE)) { + mPost.setVoteType(1); + mUpvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.backgroundColorPrimaryDark), android.graphics.PorterDuff.Mode.SRC_IN); + } else { + mPost.setVoteType(0); + mUpvoteButton.clearColorFilter(); + } + + mDownvoteButton.clearColorFilter(); + mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); + + mUpdatePostInPostFragmentCallback.updatePost(mPost); + } + + @Override + public void onVoteThingFail() { + Toast.makeText(mActivity, R.string.vote_failed, Toast.LENGTH_SHORT).show(); + mPost.setVoteType(previousVoteType); + mScoreTextView.setText(Integer.toString(mPost.getScore() + previousVoteType)); + mUpvoteButton.setColorFilter(previousUpvoteButtonColorFilter); + mDownvoteButton.setColorFilter(previousDownvoteButtonColorFilter); + + mUpdatePostInPostFragmentCallback.updatePost(mPost); + } + }, mPost.getFullName(), newVoteType); + }); + + mDownvoteButton.setOnClickListener(view -> { + ColorFilter previousUpvoteButtonColorFilter = mUpvoteButton.getColorFilter(); + ColorFilter previousDownvoteButtonColorFilter = mDownvoteButton.getColorFilter(); + int previousVoteType = mPost.getVoteType(); + String newVoteType; + + mUpvoteButton.clearColorFilter(); + + if(previousDownvoteButtonColorFilter == null) { + //Not upvoted before + mPost.setVoteType(-1); + newVoteType = RedditUtils.DIR_DOWNVOTE; + mDownvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN); + } else { + //Upvoted before + mPost.setVoteType(0); + newVoteType = RedditUtils.DIR_UNVOTE; + mDownvoteButton.clearColorFilter(); + } + + mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); + + mUpdatePostInPostFragmentCallback.updatePost(mPost); + + VoteThing.voteThing(mOauthRetrofit, mSharedPreferences, new VoteThing.VoteThingWithoutPositionListener() { + @Override + public void onVoteThingSuccess() { + if(newVoteType.equals(RedditUtils.DIR_DOWNVOTE)) { + mPost.setVoteType(-1); + mDownvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN); + } else { + mPost.setVoteType(0); + mDownvoteButton.clearColorFilter(); + } + + mUpvoteButton.clearColorFilter(); + mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); + + mUpdatePostInPostFragmentCallback.updatePost(mPost); + } + + @Override + public void onVoteThingFail() { + Toast.makeText(mActivity, R.string.vote_failed, Toast.LENGTH_SHORT).show(); + mPost.setVoteType(previousVoteType); + mScoreTextView.setText(Integer.toString(mPost.getScore() + previousVoteType)); + mUpvoteButton.setColorFilter(previousUpvoteButtonColorFilter); + mDownvoteButton.setColorFilter(previousDownvoteButtonColorFilter); + + mUpdatePostInPostFragmentCallback.updatePost(mPost); + } + }, mPost.getFullName(), newVoteType); + }); + } } class CommentViewHolder extends RecyclerView.ViewHolder { @@ -242,117 +753,117 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter { Intent intent = new Intent(mActivity, ViewUserDetailActivity.class); - intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, mVisibleComments.get(getAdapterPosition()).getAuthor()); + intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, mVisibleComments.get(getAdapterPosition() - 1).getAuthor()); mActivity.startActivity(intent); }); shareButton.setOnClickListener(view -> { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); - String extraText = mVisibleComments.get(getAdapterPosition()).getPermalink(); + String extraText = mVisibleComments.get(getAdapterPosition() - 1).getPermalink(); intent.putExtra(Intent.EXTRA_TEXT, extraText); mActivity.startActivity(Intent.createChooser(intent, "Share")); }); expandButton.setOnClickListener(view -> { - if(mVisibleComments.get(getAdapterPosition()).isExpanded()) { + if(mVisibleComments.get(getAdapterPosition() - 1).isExpanded()) { collapseChildren(getAdapterPosition()); expandButton.setImageResource(R.drawable.ic_expand_more_black_20dp); } else { expandChildren(getAdapterPosition()); - mVisibleComments.get(getAdapterPosition()).setExpanded(true); + mVisibleComments.get(getAdapterPosition() - 1).setExpanded(true); expandButton.setImageResource(R.drawable.ic_expand_less_black_20dp); } }); replyButton.setOnClickListener(view -> { Intent intent = new Intent(mActivity, CommentActivity.class); - intent.putExtra(CommentActivity.EXTRA_PARENT_DEPTH_KEY, mVisibleComments.get(getAdapterPosition()).getDepth() + 1); - intent.putExtra(CommentActivity.EXTRA_COMMENT_PARENT_TEXT_KEY, mVisibleComments.get(getAdapterPosition()).getCommentContent()); - intent.putExtra(CommentActivity.EXTRA_PARENT_FULLNAME_KEY, mVisibleComments.get(getAdapterPosition()).getFullName()); + intent.putExtra(CommentActivity.EXTRA_PARENT_DEPTH_KEY, mVisibleComments.get(getAdapterPosition() - 1).getDepth() + 1); + intent.putExtra(CommentActivity.EXTRA_COMMENT_PARENT_TEXT_KEY, mVisibleComments.get(getAdapterPosition() - 1).getCommentContent()); + intent.putExtra(CommentActivity.EXTRA_PARENT_FULLNAME_KEY, mVisibleComments.get(getAdapterPosition() - 1).getFullName()); intent.putExtra(CommentActivity.EXTRA_IS_REPLYING_KEY, true); intent.putExtra(CommentActivity.EXTRA_PARENT_POSITION_KEY, getAdapterPosition()); mActivity.startActivityForResult(intent, CommentActivity.WRITE_COMMENT_REQUEST_CODE); }); upvoteButton.setOnClickListener(view -> { - int previousVoteType = mVisibleComments.get(getAdapterPosition()).getVoteType(); + int previousVoteType = mVisibleComments.get(getAdapterPosition() - 1).getVoteType(); String newVoteType; downvoteButton.clearColorFilter(); if(previousVoteType != CommentData.VOTE_TYPE_UPVOTE) { //Not upvoted before - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_UPVOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_UPVOTE); newVoteType = RedditUtils.DIR_UPVOTE; upvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.backgroundColorPrimaryDark), android.graphics.PorterDuff.Mode.SRC_IN); } else { //Upvoted before - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); newVoteType = RedditUtils.DIR_UNVOTE; upvoteButton.clearColorFilter(); } - scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition()).getScore() + mVisibleComments.get(getAdapterPosition()).getVoteType())); + scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition() - 1).getScore() + mVisibleComments.get(getAdapterPosition() - 1).getVoteType())); VoteThing.voteThing(mOauthRetrofit, mSharedPreferences, new VoteThing.VoteThingListener() { @Override public void onVoteThingSuccess(int position) { if(newVoteType.equals(RedditUtils.DIR_UPVOTE)) { - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_UPVOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_UPVOTE); upvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.backgroundColorPrimaryDark), android.graphics.PorterDuff.Mode.SRC_IN); } else { - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); upvoteButton.clearColorFilter(); } downvoteButton.clearColorFilter(); - scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition()).getScore() + mVisibleComments.get(getAdapterPosition()).getVoteType())); + scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition() - 1).getScore() + mVisibleComments.get(getAdapterPosition() - 1).getVoteType())); } @Override public void onVoteThingFail(int position) { } - }, mVisibleComments.get(getAdapterPosition()).getFullName(), newVoteType, getAdapterPosition()); + }, mVisibleComments.get(getAdapterPosition() - 1).getFullName(), newVoteType, getAdapterPosition()); }); downvoteButton.setOnClickListener(view -> { - int previousVoteType = mVisibleComments.get(getAdapterPosition()).getVoteType(); + int previousVoteType = mVisibleComments.get(getAdapterPosition() - 1).getVoteType(); String newVoteType; upvoteButton.clearColorFilter(); if(previousVoteType != CommentData.VOTE_TYPE_DOWNVOTE) { //Not downvoted before - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_DOWNVOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_DOWNVOTE); newVoteType = RedditUtils.DIR_DOWNVOTE; downvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN); } else { //Downvoted before - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); newVoteType = RedditUtils.DIR_UNVOTE; downvoteButton.clearColorFilter(); } - scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition()).getScore() + mVisibleComments.get(getAdapterPosition()).getVoteType())); + scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition() - 1).getScore() + mVisibleComments.get(getAdapterPosition() - 1).getVoteType())); VoteThing.voteThing(mOauthRetrofit, mSharedPreferences, new VoteThing.VoteThingListener() { @Override public void onVoteThingSuccess(int position1) { if(newVoteType.equals(RedditUtils.DIR_DOWNVOTE)) { - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_DOWNVOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_DOWNVOTE); downvoteButton.setColorFilter(ContextCompat.getColor(mActivity, R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN); } else { - mVisibleComments.get(getAdapterPosition()).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); + mVisibleComments.get(getAdapterPosition() - 1).setVoteType(CommentData.VOTE_TYPE_NO_VOTE); downvoteButton.clearColorFilter(); } upvoteButton.clearColorFilter(); - scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition()).getScore() + mVisibleComments.get(getAdapterPosition()).getVoteType())); + scoreTextView.setText(Integer.toString(mVisibleComments.get(getAdapterPosition() - 1).getScore() + mVisibleComments.get(getAdapterPosition() - 1).getVoteType())); } @Override public void onVoteThingFail(int position1) { } - }, mVisibleComments.get(getAdapterPosition()).getFullName(), newVoteType, getAdapterPosition()); + }, mVisibleComments.get(getAdapterPosition() - 1).getFullName(), newVoteType, getAdapterPosition()); }); } } @@ -366,11 +877,11 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter { - int parentPosition = getParentPosition(getAdapterPosition()); + int parentPosition = getParentPosition(getAdapterPosition() - 1); CommentData parentComment = mVisibleComments.get(parentPosition); - mVisibleComments.get(getAdapterPosition()).setLoadingMoreChildren(true); - mVisibleComments.get(getAdapterPosition()).setLoadMoreChildrenFailed(false); + mVisibleComments.get(getAdapterPosition() - 1).setLoadingMoreChildren(true); + mVisibleComments.get(getAdapterPosition() - 1).setLoadMoreChildrenFailed(false); placeholderTextView.setText(R.string.loading); FetchComment.fetchMoreComment(mRetrofit, mSubredditNamePrefixed, parentComment.getMoreChildrenFullnames(), @@ -389,8 +900,8 @@ class CommentRecyclerViewAdapter extends RecyclerView.Adapter call, Throwable t) { + public void onFailure(@NonNull Call call, @NonNull Throwable t) { Log.i("more comment failed", t.getMessage()); fetchMoreCommentListener.onFetchMoreCommentFailed(); } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/LoadSubredditIconAsyncTask.java b/app/src/main/java/ml/docilealligator/infinityforreddit/LoadSubredditIconAsyncTask.java index f7f0fdcd..923e657f 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/LoadSubredditIconAsyncTask.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/LoadSubredditIconAsyncTask.java @@ -34,6 +34,8 @@ class LoadSubredditIconAsyncTask extends AsyncTask { @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); - loadSubredditIconAsyncTaskListener.loadIconSuccess(iconImageUrl); + if(!isCancelled()) { + loadSubredditIconAsyncTaskListener.loadIconSuccess(iconImageUrl); + } } } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java b/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java index 72f8c4a3..a10579c1 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java @@ -16,7 +16,7 @@ public interface RedditAPI { @POST("api/v1/access_token") Call getAccessToken(@HeaderMap Map headers, @FieldMap Map params); - @GET("{subredditNamePrefixed}/comments/{article}.json?raw_json=1") + @GET("{subredditNamePrefixed}/comments/{article}.json?&raw_json=1") Call getComments(@Path("subredditNamePrefixed") String subredditNamePrefixed, @Path("article") String article); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java index 8ab0b3f5..0eb99830 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java @@ -3,46 +3,23 @@ package ml.docilealligator.infinityforreddit; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.ColorFilter; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; -import androidx.browser.customtabs.CustomTabsIntent; import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.content.ContextCompat; -import androidx.core.widget.NestedScrollView; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; -import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.RequestManager; -import com.bumptech.glide.load.DataSource; -import com.bumptech.glide.load.engine.GlideException; -import com.bumptech.glide.request.RequestListener; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.Target; -import com.google.android.material.card.MaterialCardView; -import com.google.android.material.chip.Chip; import com.google.android.material.snackbar.Snackbar; -import com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar; -import com.santalu.aspectratioimageview.AspectRatioImageView; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -53,15 +30,9 @@ import java.util.Locale; import javax.inject.Inject; import javax.inject.Named; -import CustomView.AspectRatioGifImageView; -import SubredditDatabase.SubredditRoomDatabase; import butterknife.BindView; import butterknife.ButterKnife; -import jp.wasabeef.glide.transformations.BlurTransformation; -import jp.wasabeef.glide.transformations.RoundedCornersTransformation; import retrofit2.Retrofit; -import ru.noties.markwon.SpannableConfiguration; -import ru.noties.markwon.view.MarkwonView; import static ml.docilealligator.infinityforreddit.CommentActivity.EXTRA_COMMENT_DATA_KEY; import static ml.docilealligator.infinityforreddit.CommentActivity.WRITE_COMMENT_REQUEST_CODE; @@ -93,34 +64,7 @@ public class ViewPostDetailActivity extends AppCompatActivity { @BindView(R.id.coordinator_layout_view_post_detail) CoordinatorLayout mCoordinatorLayout; @BindView(R.id.toolbar_view_post_detail_activity) Toolbar toolbar; - @BindView(R.id.nested_scroll_view_view_post_detail_activity) NestedScrollView mNestedScrollView; - @BindView(R.id.subreddit_icon_name_linear_layout_view_post_detail) LinearLayout mSubredditIconNameLinearLayout; - @BindView(R.id.subreddit_icon_circle_image_view_view_post_detail) AspectRatioGifImageView mSubredditIconGifImageView; - @BindView(R.id.subreddit_text_view_view_post_detail) TextView mSubredditTextView; - @BindView(R.id.post_time_text_view_view_post_detail) TextView mPostTimeTextView; - @BindView(R.id.title_text_view_view_post_detail) TextView mTitleTextView; - @BindView(R.id.content_markdown_view_view_post_detail) MarkwonView mContentMarkdownView; - @BindView(R.id.type_text_view_view_post_detail) Chip mTypeChip; - @BindView(R.id.gilded_image_view_view_post_detail) ImageView mGildedImageView; - @BindView(R.id.gilded_number_text_view_view_post_detail) TextView mGildedNumberTextView; - @BindView(R.id.crosspost_image_view_view_post_detail) ImageView mCrosspostImageView; - @BindView(R.id.nsfw_text_view_view_post_detail) Chip mNSFWChip; - @BindView(R.id.link_text_view_view_post_detail) TextView linkTextView; - @BindView(R.id.image_view_wrapper_view_post_detail) RelativeLayout mRelativeLayout; - @BindView(R.id.load_wrapper_view_post_detail) RelativeLayout mLoadWrapper; - @BindView(R.id.progress_bar_view_post_detail) ProgressBar mLoadImageProgressBar; - @BindView(R.id.load_image_error_text_view_view_post_detail) TextView mLoadImageErrorTextView; - @BindView(R.id.image_view_view_post_detail) AspectRatioImageView mImageView; - @BindView(R.id.image_view_no_preview_link_view_post_detail) ImageView mNoPreviewLinkImageView; - @BindView(R.id.plus_button_view_post_detail) ImageView mUpvoteButton; - @BindView(R.id.score_text_view_view_post_detail) TextView mScoreTextView; - @BindView(R.id.minus_button_view_post_detail) ImageView mDownvoteButton; - @BindView(R.id.share_button_view_post_detail) ImageView mShareButton; - @BindView(R.id.comment_progress_bar_view_post_detail) CircleProgressBar mCommentProgressbar; - @BindView(R.id.comment_card_view_view_post_detail) MaterialCardView mCommentCardView; @BindView(R.id.recycler_view_view_post_detail) RecyclerView mRecyclerView; - @BindView(R.id.no_comment_wrapper_linear_layout_view_post_detail) LinearLayout mNoCommentWrapperLinearLayout; - @BindView(R.id.no_comment_image_view_view_post_detail) ImageView mNoCommentImageView; @Inject @Named("no_oauth") Retrofit mRetrofit; @@ -148,14 +92,26 @@ public class ViewPostDetailActivity extends AppCompatActivity { mGlide = Glide.with(this); mLocale = getResources().getConfiguration().locale; + mRecyclerView.setNestedScrollingEnabled(false); + mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); + mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); + if(savedInstanceState == null) { orientation = getResources().getConfiguration().orientation; mPost = getIntent().getExtras().getParcelable(EXTRA_POST_DATA); } else { orientation = savedInstanceState.getInt(ORIENTATION_STATE); mPost = savedInstanceState.getParcelable(POST_STATE); - isRefreshing = savedInstanceState.getBoolean(IS_REFRESHING_STATE); + } + mAdapter = new CommentRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit, + mOauthRetrofit, mGlide, mSharedPreferences, mPost, + mPost.getSubredditNamePrefixed(), mLocale, mLoadSubredditIconAsyncTask, + post -> EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition))); + mRecyclerView.setAdapter(mAdapter); + + if(savedInstanceState != null) { + isRefreshing = savedInstanceState.getBoolean(IS_REFRESHING_STATE); if(isRefreshing) { isRefreshing = false; refresh(); @@ -166,336 +122,11 @@ public class ViewPostDetailActivity extends AppCompatActivity { postListPosition = getIntent().getExtras().getInt(EXTRA_POST_LIST_POSITION); } - mRecyclerView.setNestedScrollingEnabled(false); - mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); - mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)); - fetchComment(); - bindView(); - initializeButtonOnClickListener(); - } - - private void bindView() { - mTitleTextView.setText(mPost.getTitle()); - - if(mPost.getSubredditIconUrl() == null) { - mLoadSubredditIconAsyncTask = new LoadSubredditIconAsyncTask( - SubredditRoomDatabase.getDatabase(this).subredditDao(), mPost.getSubredditNamePrefixed().substring(2), - iconImageUrl -> { - if(!iconImageUrl.equals("")) { - mGlide.load(iconImageUrl) - .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) - .error(mGlide.load(R.drawable.subreddit_default_icon) - .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))) - .into(mSubredditIconGifImageView); - } else { - mGlide.load(R.drawable.subreddit_default_icon) - .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) - .into(mSubredditIconGifImageView); - } - - mPost.setSubredditIconUrl(iconImageUrl); - }); - mLoadSubredditIconAsyncTask.execute(); - } else if(!mPost.getSubredditIconUrl().equals("")) { - mGlide.load(mPost.getSubredditIconUrl()) - .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) - .error(mGlide.load(R.drawable.subreddit_default_icon) - .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))) - .into(mSubredditIconGifImageView); - } else { - mGlide.load(R.drawable.subreddit_default_icon) - .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) - .into(mSubredditIconGifImageView); - } - - switch (mPost.getVoteType()) { - case 1: - //Upvote - mUpvoteButton.setColorFilter(ContextCompat.getColor(this, R.color.backgroundColorPrimaryDark), PorterDuff.Mode.SRC_IN); - break; - case -1: - //Downvote - mDownvoteButton.setColorFilter(ContextCompat.getColor(this, R.color.minusButtonColor), PorterDuff.Mode.SRC_IN); - break; - case 0: - mUpvoteButton.clearColorFilter(); - mDownvoteButton.clearColorFilter(); - } - - if(mPost.getPostType() != Post.TEXT_TYPE && mPost.getPostType() != Post.NO_PREVIEW_LINK_TYPE) { - mRelativeLayout.setVisibility(View.VISIBLE); - mImageView.setVisibility(View.VISIBLE); - mImageView.setRatio((float) mPost.getPreviewHeight() / (float) mPost.getPreviewWidth()); - loadImage(); - } else { - mRelativeLayout.setVisibility(View.GONE); - mImageView.setVisibility(View.GONE); - } - - if(mPost.isCrosspost()) { - mCrosspostImageView.setVisibility(View.VISIBLE); - } - - mSubredditTextView.setText(mPost.getSubredditNamePrefixed()); - - mPostTimeTextView.setText(mPost.getPostTime()); - - if(mPost.getGilded() > 0) { - mGildedImageView.setVisibility(View.VISIBLE); - mGlide.load(R.drawable.gold).into(mGildedImageView); - mGildedNumberTextView.setVisibility(View.VISIBLE); - String gildedNumber = getResources().getString(R.string.gilded, mPost.getGilded()); - mGildedNumberTextView.setText(gildedNumber); - } - - if(mPost.isNSFW()) { - mNSFWChip.setVisibility(View.VISIBLE); - } else { - mNSFWChip.setVisibility(View.GONE); - } - - String scoreWithVote = Integer.toString(mPost.getScore() + mPost.getVoteType()); - mScoreTextView.setText(scoreWithVote); - - switch (mPost.getPostType()) { - case Post.IMAGE_TYPE: - mTypeChip.setText("IMAGE"); - - mImageView.setOnClickListener(view -> { - Intent intent = new Intent(ViewPostDetailActivity.this, ViewImageActivity.class); - intent.putExtra(ViewImageActivity.IMAGE_URL_KEY, mPost.getUrl()); - intent.putExtra(ViewImageActivity.TITLE_KEY, mPost.getTitle()); - intent.putExtra(ViewImageActivity.FILE_NAME_KEY, mPost.getSubredditNamePrefixed().substring(2) - + "-" + mPost.getId().substring(3)); - startActivity(intent); - }); - break; - case Post.LINK_TYPE: - mTypeChip.setText("LINK"); - - linkTextView.setVisibility(View.VISIBLE); - String domain = Uri.parse(mPost.getUrl()).getHost(); - linkTextView.setText(domain); - - mImageView.setOnClickListener(view -> { - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); - // add share action to menu list - builder.addDefaultShareMenuItem(); - builder.setToolbarColor(getResources().getColor(R.color.colorPrimary)); - CustomTabsIntent customTabsIntent = builder.build(); - customTabsIntent.launchUrl(ViewPostDetailActivity.this, Uri.parse(mPost.getUrl())); - }); - break; - case Post.GIF_VIDEO_TYPE: - mTypeChip.setText("GIF"); - - final Uri gifVideoUri = Uri.parse(mPost.getVideoUrl()); - mImageView.setOnClickListener(view -> { - Intent intent = new Intent(ViewPostDetailActivity.this, ViewVideoActivity.class); - intent.setData(gifVideoUri); - intent.putExtra(ViewVideoActivity.TITLE_KEY, mPost.getTitle()); - intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, mPost.isDashVideo()); - intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, mPost.isDownloadableGifOrVideo()); - if(mPost.isDownloadableGifOrVideo()) { - intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, mPost.getGifOrVideoDownloadUrl()); - intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, mPost.getSubredditNamePrefixed()); - intent.putExtra(ViewVideoActivity.ID_KEY, mPost.getId()); - } - startActivity(intent); - }); - break; - case Post.VIDEO_TYPE: - mTypeChip.setText("VIDEO"); - - final Uri videoUri = Uri.parse(mPost.getVideoUrl()); - mImageView.setOnClickListener(view -> { - Intent intent = new Intent(ViewPostDetailActivity.this, ViewVideoActivity.class); - intent.setData(videoUri); - intent.putExtra(ViewVideoActivity.TITLE_KEY, mPost.getTitle()); - intent.putExtra(ViewVideoActivity.IS_DASH_VIDEO_KEY, mPost.isDashVideo()); - intent.putExtra(ViewVideoActivity.IS_DOWNLOADABLE_KEY, mPost.isDownloadableGifOrVideo()); - if(mPost.isDownloadableGifOrVideo()) { - intent.putExtra(ViewVideoActivity.DOWNLOAD_URL_KEY, mPost.getGifOrVideoDownloadUrl()); - intent.putExtra(ViewVideoActivity.SUBREDDIT_KEY, mPost.getSubredditNamePrefixed()); - intent.putExtra(ViewVideoActivity.ID_KEY, mPost.getId()); - } - startActivity(intent); - }); - break; - case Post.NO_PREVIEW_LINK_TYPE: - mTypeChip.setText("LINK"); - - linkTextView.setVisibility(View.VISIBLE); - String noPreviewLinkDomain = Uri.parse(mPost.getUrl()).getHost(); - linkTextView.setText(noPreviewLinkDomain); - - if(!mPost.getSelfText().equals("")) { - mContentMarkdownView.setVisibility(View.VISIBLE); - mContentMarkdownView.setMarkdown(getCustomSpannableConfiguration(), mPost.getSelfText()); - } - mNoPreviewLinkImageView.setVisibility(View.VISIBLE); - mNoPreviewLinkImageView.setOnClickListener(view -> { - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); - // add share action to menu list - builder.addDefaultShareMenuItem(); - builder.setToolbarColor(getResources().getColor(R.color.colorPrimary)); - CustomTabsIntent customTabsIntent = builder.build(); - customTabsIntent.launchUrl(ViewPostDetailActivity.this, Uri.parse(mPost.getUrl())); - }); - break; - case Post.TEXT_TYPE: - mTypeChip.setText("TEXT"); - - if(!mPost.getSelfText().equals("")) { - mContentMarkdownView.setVisibility(View.VISIBLE); - mContentMarkdownView.setMarkdown(getCustomSpannableConfiguration(), mPost.getSelfText()); - } - break; - } - } - - private void initializeButtonOnClickListener() { - mSubredditIconNameLinearLayout.setOnClickListener(view -> { - Intent intent = new Intent(ViewPostDetailActivity.this, ViewSubredditDetailActivity.class); - intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, - mPost.getSubredditNamePrefixed().substring(2)); - startActivity(intent); - }); - - mShareButton.setOnClickListener(view -> { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - String extraText = mPost.getTitle() + "\n" + mPost.getPermalink(); - intent.putExtra(Intent.EXTRA_TEXT, extraText); - startActivity(Intent.createChooser(intent, "Share")); - }); - - mUpvoteButton.setOnClickListener(view -> { - ColorFilter previousUpvoteButtonColorFilter = mUpvoteButton.getColorFilter(); - ColorFilter previousDownvoteButtonColorFilter = mDownvoteButton.getColorFilter(); - int previousVoteType = mPost.getVoteType(); - String newVoteType; - - mDownvoteButton.clearColorFilter(); - - if(previousUpvoteButtonColorFilter == null) { - //Not upvoted before - mPost.setVoteType(1); - newVoteType = RedditUtils.DIR_UPVOTE; - mUpvoteButton.setColorFilter(ContextCompat.getColor(this, R.color.backgroundColorPrimaryDark), android.graphics.PorterDuff.Mode.SRC_IN); - } else { - //Upvoted before - mPost.setVoteType(0); - newVoteType = RedditUtils.DIR_UNVOTE; - mUpvoteButton.clearColorFilter(); - } - - mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); - - if(postListPosition != -1) { - EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); - } - - VoteThing.voteThing(mOauthRetrofit, mSharedPreferences, new VoteThing.VoteThingWithoutPositionListener() { - @Override - public void onVoteThingSuccess() { - if(newVoteType.equals(RedditUtils.DIR_UPVOTE)) { - mPost.setVoteType(1); - mUpvoteButton.setColorFilter(ContextCompat.getColor(ViewPostDetailActivity.this, R.color.backgroundColorPrimaryDark), android.graphics.PorterDuff.Mode.SRC_IN); - } else { - mPost.setVoteType(0); - mUpvoteButton.clearColorFilter(); - } - - mDownvoteButton.clearColorFilter(); - mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); - - if(postListPosition != -1) { - EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); - } - } - - @Override - public void onVoteThingFail() { - Toast.makeText(ViewPostDetailActivity.this, R.string.vote_failed, Toast.LENGTH_SHORT).show(); - mPost.setVoteType(previousVoteType); - mScoreTextView.setText(Integer.toString(mPost.getScore() + previousVoteType)); - mUpvoteButton.setColorFilter(previousUpvoteButtonColorFilter); - mDownvoteButton.setColorFilter(previousDownvoteButtonColorFilter); - - if(postListPosition != -1) { - EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); - } - } - }, mPost.getFullName(), newVoteType); - }); - - mDownvoteButton.setOnClickListener(view -> { - ColorFilter previousUpvoteButtonColorFilter = mUpvoteButton.getColorFilter(); - ColorFilter previousDownvoteButtonColorFilter = mDownvoteButton.getColorFilter(); - int previousVoteType = mPost.getVoteType(); - String newVoteType; - - mUpvoteButton.clearColorFilter(); - - if(previousDownvoteButtonColorFilter == null) { - //Not upvoted before - mPost.setVoteType(-1); - newVoteType = RedditUtils.DIR_DOWNVOTE; - mDownvoteButton.setColorFilter(ContextCompat.getColor(this, R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN); - } else { - //Upvoted before - mPost.setVoteType(0); - newVoteType = RedditUtils.DIR_UNVOTE; - mDownvoteButton.clearColorFilter(); - } - - mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); - - if(postListPosition != -1) { - EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); - } - - VoteThing.voteThing(mOauthRetrofit, mSharedPreferences, new VoteThing.VoteThingWithoutPositionListener() { - @Override - public void onVoteThingSuccess() { - if(newVoteType.equals(RedditUtils.DIR_DOWNVOTE)) { - mPost.setVoteType(-1); - mDownvoteButton.setColorFilter(ContextCompat.getColor(ViewPostDetailActivity.this, R.color.colorAccent), android.graphics.PorterDuff.Mode.SRC_IN); - } else { - mPost.setVoteType(0); - mDownvoteButton.clearColorFilter(); - } - - mUpvoteButton.clearColorFilter(); - mScoreTextView.setText(Integer.toString(mPost.getScore() + mPost.getVoteType())); - - if(postListPosition != -1) { - EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); - } - } - - @Override - public void onVoteThingFail() { - Toast.makeText(ViewPostDetailActivity.this, R.string.vote_failed, Toast.LENGTH_SHORT).show(); - mPost.setVoteType(previousVoteType); - mScoreTextView.setText(Integer.toString(mPost.getScore() + previousVoteType)); - mUpvoteButton.setColorFilter(previousUpvoteButtonColorFilter); - mDownvoteButton.setColorFilter(previousDownvoteButtonColorFilter); - - if(postListPosition != -1) { - EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); - } - } - }, mPost.getFullName(), newVoteType); - }); } private void fetchComment() { - mCommentCardView.setVisibility(View.GONE); - mCommentProgressbar.setVisibility(View.VISIBLE); - mNoCommentWrapperLinearLayout.setVisibility(View.GONE); + mAdapter.initiallyLoading(); FetchComment.fetchComment(mRetrofit, mPost.getSubredditNamePrefixed(), mPost.getId(), mLocale, new FetchComment.FetchCommentListener() { @@ -503,7 +134,19 @@ public class ViewPostDetailActivity extends AppCompatActivity { public void onFetchCommentSuccess(ArrayList expandedComments, String parentId, ArrayList children) { ViewPostDetailActivity.this.children = children; - mCommentProgressbar.setVisibility(View.GONE); + + mAdapter.addComments(expandedComments); + + mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + + } + }); + + /*mCommentProgressbar.setVisibility(View.GONE); if (expandedComments.size() > 0) { if(mAdapter == null) { @@ -520,26 +163,34 @@ public class ViewPostDetailActivity extends AppCompatActivity { } mAdapter = new CommentRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit, - mOauthRetrofit, mSharedPreferences, expandedComments, - mPost.getSubredditNamePrefixed(), mLocale); + mOauthRetrofit, mGlide, mSharedPreferences, mPost, + mPost.getSubredditNamePrefixed(), mLocale, new CommentRecyclerViewAdapter.UpdatePostInPostFragmentCallback() { + @Override + public void updatePost(Post post) { + EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); + } + }); mRecyclerView.setAdapter(mAdapter); - mCommentCardView.setVisibility(View.VISIBLE); + //mCommentCardView.setVisibility(View.VISIBLE); } else { mNoCommentWrapperLinearLayout.setVisibility(View.VISIBLE); - mGlide.load(R.drawable.no_comment_indicator).into(mNoCommentImageView); - } + mGlide.load(R.drawable.no_comment_placeholder).into(mNoCommentImageView); + }*/ } @Override public void onFetchCommentFailed() { - mCommentProgressbar.setVisibility(View.GONE); - showRetrySnackbar(); + mAdapter.initiallyLoadCommentsFailed(); } }); } private void fetchMoreComment(int startingIndex) { + if(isLoadingMoreChildren) { + return; + } + isLoadingMoreChildren = true; FetchComment.fetchMoreComment(mRetrofit, mPost.getSubredditNamePrefixed(), children, startingIndex, 0, mLocale, new FetchComment.FetchMoreCommentListener() { @@ -560,73 +211,11 @@ public class ViewPostDetailActivity extends AppCompatActivity { }); } - private void loadImage() { - RequestBuilder imageRequestBuilder = mGlide.load(mPost.getPreviewUrl()) - .apply(new RequestOptions().override(mPost.getPreviewWidth(), mPost.getPreviewHeight())) - .listener(new RequestListener() { - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { - mLoadImageProgressBar.setVisibility(View.GONE); - mLoadImageErrorTextView.setVisibility(View.VISIBLE); - mLoadImageErrorTextView.setOnClickListener(view -> { - mLoadImageProgressBar.setVisibility(View.VISIBLE); - mLoadImageErrorTextView.setVisibility(View.GONE); - loadImage(); - }); - return false; - } - - @Override - public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { - mLoadWrapper.setVisibility(View.GONE); - return false; - } - }); - - if(mPost.isNSFW()) { - imageRequestBuilder.apply(RequestOptions.bitmapTransform(new BlurTransformation(50, 2))) - .into(mImageView); - } else { - imageRequestBuilder.into(mImageView); - } - } - - private SpannableConfiguration getCustomSpannableConfiguration() { - return SpannableConfiguration.builder(this).linkResolver((view, link) -> { - if(link.startsWith("/u/") || link.startsWith("u/")) { - Intent intent = new Intent(ViewPostDetailActivity.this, ViewUserDetailActivity.class); - intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, link.substring(3)); - startActivity(intent); - } else if(link.startsWith("/r/") || link.startsWith("r/")) { - Intent intent = new Intent(ViewPostDetailActivity.this, ViewSubredditDetailActivity.class); - intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, link.substring(3)); - startActivity(intent); - } else { - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); - // add share action to menu list - builder.addDefaultShareMenuItem(); - builder.setToolbarColor(getResources().getColor(R.color.colorPrimary)); - CustomTabsIntent customTabsIntent = builder.build(); - customTabsIntent.launchUrl(ViewPostDetailActivity.this, Uri.parse(link)); - } - }).build(); - } - - private void showRetrySnackbar() { - Snackbar snackbar = Snackbar.make(mCoordinatorLayout, R.string.post_load_comments_failed, Snackbar.LENGTH_INDEFINITE); - snackbar.setAction(R.string.retry, view -> fetchComment()); - snackbar.show(); - } - private void refresh() { if(!isRefreshing) { isRefreshing = true; mChildrenStartingIndex = 0; - if(mAdapter != null) { - mAdapter.clearData(); - } - fetchComment(); String accessToken = getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE) @@ -636,7 +225,7 @@ public class ViewPostDetailActivity extends AppCompatActivity { @Override public void fetchPostSuccess(Post post) { mPost = post; - bindView(); + mAdapter.updatePost(mPost); EventBus.getDefault().post(new PostUpdateEventToPostList(mPost, postListPosition)); isRefreshing = false; } @@ -654,7 +243,7 @@ public class ViewPostDetailActivity extends AppCompatActivity { public void onPostUpdateEvent(PostUpdateEventToDetailActivity event) { if(mPost.getId().equals(event.postId)) { mPost.setVoteType(event.voteType); - mScoreTextView.setText(Integer.toString(mPost.getScore() + event.voteType)); + mAdapter.updatePost(mPost); } } diff --git a/app/src/main/res/drawable/no_comment_indicator.png b/app/src/main/res/drawable/no_comment_placeholder.png similarity index 100% rename from app/src/main/res/drawable/no_comment_indicator.png rename to app/src/main/res/drawable/no_comment_placeholder.png diff --git a/app/src/main/res/layout/activity_view_post_detail.xml b/app/src/main/res/layout/activity_view_post_detail.xml index 0c9f6786..7416ed7a 100644 --- a/app/src/main/res/layout/activity_view_post_detail.xml +++ b/app/src/main/res/layout/activity_view_post_detail.xml @@ -21,339 +21,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_load_comments.xml b/app/src/main/res/layout/item_load_comments.xml new file mode 100644 index 00000000..06bc8c38 --- /dev/null +++ b/app/src/main/res/layout/item_load_comments.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_load_comments_failed_placeholder.xml b/app/src/main/res/layout/item_load_comments_failed_placeholder.xml new file mode 100644 index 00000000..476d3d2d --- /dev/null +++ b/app/src/main/res/layout/item_load_comments_failed_placeholder.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_no_comment_placeholder.xml b/app/src/main/res/layout/item_no_comment_placeholder.xml new file mode 100644 index 00000000..becf93d4 --- /dev/null +++ b/app/src/main/res/layout/item_no_comment_placeholder.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_post_detail.xml b/app/src/main/res/layout/item_post_detail.xml new file mode 100644 index 00000000..870d21ef --- /dev/null +++ b/app/src/main/res/layout/item_post_detail.xml @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file