From af870c157bd542b776bf16ff36527b80af5f92d3 Mon Sep 17 00:00:00 2001 From: Docile-Alligator <25734209+Docile-Alligator@users.noreply.github.com> Date: Mon, 14 Nov 2022 18:44:07 +1100 Subject: Swipe to see gallery images in PostDetailRecyclerViewAdapter. --- .../adapters/PostDetailRecyclerViewAdapter.java | 223 ++++++++++++--------- .../PostGalleryTypeImageRecyclerViewAdapter.java | 54 +++++ .../customviews/SwipeLockFrameLayout.java | 76 ------- .../customviews/SwipeLockLinearLayout.java | 76 +++++++ 4 files changed, 256 insertions(+), 173 deletions(-) delete mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockFrameLayout.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockLinearLayout.java (limited to 'app/src/main/java/ml/docilealligator') 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 df6603d5..35606d65 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostDetailRecyclerViewAdapter.java @@ -15,10 +15,12 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.text.Spanned; -import android.text.TextUtils; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RelativeLayout; @@ -31,6 +33,7 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.PagerSnapHelper; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.RequestBuilder; @@ -130,7 +133,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter previews) { Post.Preview preview; @@ -860,7 +848,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter imageRequestBuilder = mGlide.load(preview.getPreviewUrl()) @@ -888,7 +876,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter imageRequestBuilder = mGlide.load(preview.getPreviewUrl()) @@ -916,34 +904,7 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter imageRequestBuilder = mGlide.load(preview.getPreviewUrl()) - .listener(new RequestListener<>() { - @Override - public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { - ((PostDetailGalleryViewHolder) holder).mLoadImageProgressBar.setVisibility(View.GONE); - ((PostDetailGalleryViewHolder) holder).mLoadImageErrorTextView.setVisibility(View.VISIBLE); - ((PostDetailGalleryViewHolder) holder).mLoadImageErrorTextView.setOnClickListener(view -> { - ((PostDetailGalleryViewHolder) holder).mLoadImageProgressBar.setVisibility(View.VISIBLE); - ((PostDetailGalleryViewHolder) holder).mLoadImageErrorTextView.setVisibility(View.GONE); - loadImage(holder, preview); - }); - return false; - } - - @Override - public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { - ((PostDetailGalleryViewHolder) holder).mLoadWrapper.setVisibility(View.GONE); - return false; - } - }); - - if ((mPost.isNSFW() && mNeedBlurNsfw && !(mDoNotBlurNsfwInNsfwSubreddits && mFragment != null && mFragment.getIsNsfwSubreddit()) && !(mPost.getPostType() == Post.GIF_TYPE && mAutoplayNsfwVideos)) || (mPost.isSpoiler() && mNeedBlurSpoiler)) { - imageRequestBuilder.apply(RequestOptions.bitmapTransform(new BlurTransformation(50, 10))).into(((PostDetailGalleryViewHolder) holder).mImageView); - } else { - imageRequestBuilder.centerInside().downsample(mSaveMemoryCenterInsideDownSampleStrategy).into(((PostDetailGalleryViewHolder) holder).mImageView); + imageRequestBuilder.centerInside().downsample(mSaveMemoryCenterInsideDownsampleStrategy).into(((PostDetailLinkViewHolder) holder).mImageView); } } } @@ -1030,6 +991,9 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter { + adapter = new PostGalleryTypeImageRecyclerViewAdapter(mGlide, mActivity.typeface, mPostDetailMarkwon, + mSaveMemoryCenterInsideDownsampleStrategy, mColorAccent, mPrimaryTextColor, + mCardViewColor, mCommentColor, mScale); + galleryRecyclerView.setAdapter(adapter); + new PagerSnapHelper().attachToRecyclerView(galleryRecyclerView); + SwipeLockLinearLayoutManager layoutManager = new SwipeLockLinearLayoutManager( + mActivity, RecyclerView.HORIZONTAL, false, new SwipeLockInterface() { + @Override + public void lockSwipe() { + mActivity.lockSwipeRightToGoBack(); + } + + @Override + public void unlockSwipe() { + mActivity.unlockSwipeRightToGoBack(); + } + }); + galleryRecyclerView.setLayoutManager(layoutManager); + galleryRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + imageIndexTextView.setText(mActivity.getString(R.string.image_index_in_gallery, layoutManager.findFirstVisibleItemPosition() + 1, mPost.getGallery().size())); + } + }); + galleryRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() { + private float downX; + private float downY; + private boolean dragged; + private final int minTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop(); + + @Override + public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { + int action = e.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + downX = e.getRawX(); + downY = e.getRawY(); + break; + case MotionEvent.ACTION_MOVE: + if(Math.abs(e.getRawX() - downX) > minTouchSlop || Math.abs(e.getRawY() - downY) > minTouchSlop) { + dragged = true; + } + break; + case MotionEvent.ACTION_UP: + if (!dragged) { + int position = getBindingAdapterPosition(); + if (position >= 0) { + if (mPost != null) { + Intent intent = new Intent(mActivity, ViewRedditGalleryActivity.class); + intent.putParcelableArrayListExtra(ViewRedditGalleryActivity.EXTRA_REDDIT_GALLERY, mPost.getGallery()); + intent.putExtra(ViewRedditGalleryActivity.EXTRA_SUBREDDIT_NAME, mPost.getSubredditName()); + intent.putExtra(ViewRedditGalleryActivity.EXTRA_GALLERY_ITEM_INDEX, layoutManager.findFirstVisibleItemPosition()); + mActivity.startActivity(intent); + } + } + } + + downX = 0; + downY = 0; + dragged = false; + } + return false; + } + + @Override + public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) { + + } + + @Override + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + + } + }); + + mNoPreviewPostTypeImageView.setOnClickListener(view -> { Intent intent = new Intent(mActivity, ViewRedditGalleryActivity.class); intent.putParcelableArrayListExtra(ViewRedditGalleryActivity.EXTRA_REDDIT_GALLERY, mPost.getGallery()); intent.putExtra(ViewRedditGalleryActivity.EXTRA_SUBREDDIT_NAME, mPost.getSubredditName()); + intent.putExtra(ViewRedditGalleryActivity.EXTRA_GALLERY_ITEM_INDEX, layoutManager.findFirstVisibleItemPosition()); mActivity.startActivity(intent); }); - - mNoPreviewPostTypeImageView.setOnClickListener(view -> { - mImageView.performClick(); - }); } } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostGalleryTypeImageRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostGalleryTypeImageRecyclerViewAdapter.java index 6d98405a..daabf9a1 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostGalleryTypeImageRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/adapters/PostGalleryTypeImageRecyclerViewAdapter.java @@ -3,6 +3,8 @@ package ml.docilealligator.infinityforreddit.adapters; import android.content.res.ColorStateList; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,6 +24,7 @@ import com.bumptech.glide.request.target.Target; import java.util.ArrayList; +import io.noties.markwon.Markwon; import jp.wasabeef.glide.transformations.BlurTransformation; import ml.docilealligator.infinityforreddit.SaveMemoryCenterInisdeDownsampleStrategy; import ml.docilealligator.infinityforreddit.databinding.ItemGalleryImageInPostFeedBinding; @@ -30,13 +33,17 @@ import ml.docilealligator.infinityforreddit.post.Post; public class PostGalleryTypeImageRecyclerViewAdapter extends RecyclerView.Adapter { private RequestManager glide; private Typeface typeface; + private Markwon mPostDetailMarkwon; private SaveMemoryCenterInisdeDownsampleStrategy saveMemoryCenterInisdeDownsampleStrategy; private int mColorAccent; private int mPrimaryTextColor; + private int mCardViewColor; + private int mCommentColor; private float mScale; private ArrayList galleryImages; private boolean blurImage; private float ratio; + private boolean showCaption; public PostGalleryTypeImageRecyclerViewAdapter(RequestManager glide, Typeface typeface, SaveMemoryCenterInisdeDownsampleStrategy saveMemoryCenterInisdeDownsampleStrategy, @@ -47,6 +54,23 @@ public class PostGalleryTypeImageRecyclerViewAdapter extends RecyclerView.Adapte this.mColorAccent = mColorAccent; this.mPrimaryTextColor = mPrimaryTextColor; this.mScale = scale; + showCaption = false; + } + + public PostGalleryTypeImageRecyclerViewAdapter(RequestManager glide, Typeface typeface, Markwon postDetailMarkwon, + SaveMemoryCenterInisdeDownsampleStrategy saveMemoryCenterInisdeDownsampleStrategy, + int mColorAccent, int mPrimaryTextColor, int mCardViewColor, + int mCommentColor, float scale) { + this.glide = glide; + this.typeface = typeface; + this.mPostDetailMarkwon = postDetailMarkwon; + this.saveMemoryCenterInisdeDownsampleStrategy = saveMemoryCenterInisdeDownsampleStrategy; + this.mColorAccent = mColorAccent; + this.mPrimaryTextColor = mPrimaryTextColor; + this.mCardViewColor = mCardViewColor; + this.mCommentColor = mCommentColor; + this.mScale = scale; + showCaption = true; } @NonNull @@ -78,6 +102,10 @@ public class PostGalleryTypeImageRecyclerViewAdapter extends RecyclerView.Adapte } }); + if (showCaption) { + loadCaptionPreview((ImageViewHolder) holder); + } + loadImage((ImageViewHolder) holder); } } @@ -90,6 +118,11 @@ public class PostGalleryTypeImageRecyclerViewAdapter extends RecyclerView.Adapte @Override public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { super.onViewRecycled(holder); + if (holder instanceof ImageViewHolder) { + ((ImageViewHolder) holder).binding.captionConstraintLayoutItemGalleryImageInPostFeed.setVisibility(View.GONE); + ((ImageViewHolder) holder).binding.captionTextViewItemGalleryImageInPostFeed.setText(""); + ((ImageViewHolder) holder).binding.captionUrlTextViewItemGalleryImageInPostFeed.setText(""); + } } private void loadImage(ImageViewHolder holder) { @@ -116,6 +149,27 @@ public class PostGalleryTypeImageRecyclerViewAdapter extends RecyclerView.Adapte } } + private void loadCaptionPreview(ImageViewHolder holder) { + String previewCaption = galleryImages.get(holder.getBindingAdapterPosition()).caption; + String previewCaptionUrl = galleryImages.get(holder.getBindingAdapterPosition()).captionUrl; + boolean previewCaptionIsEmpty = TextUtils.isEmpty(previewCaption); + boolean previewCaptionUrlIsEmpty = TextUtils.isEmpty(previewCaptionUrl); + if (!previewCaptionIsEmpty || !previewCaptionUrlIsEmpty) { + holder.binding.captionConstraintLayoutItemGalleryImageInPostFeed.setBackgroundColor(mCardViewColor & 0x0D000000); // Make 10% darker than CardViewColor + holder.binding.captionConstraintLayoutItemGalleryImageInPostFeed.setVisibility(View.VISIBLE); + } + if (!previewCaptionIsEmpty) { + holder.binding.captionTextViewItemGalleryImageInPostFeed.setTextColor(mCommentColor); + holder.binding.captionTextViewItemGalleryImageInPostFeed.setText(previewCaption); + holder.binding.captionTextViewItemGalleryImageInPostFeed.setSelected(true); + } + if (!previewCaptionUrlIsEmpty) { + String domain = Uri.parse(previewCaptionUrl).getHost(); + domain = domain.startsWith("www.") ? domain.substring(4) : domain; + mPostDetailMarkwon.setMarkdown(holder.binding.captionUrlTextViewItemGalleryImageInPostFeed, String.format("[%s](%s)", domain, previewCaptionUrl)); + } + } + public void setGalleryImages(ArrayList galleryImages) { this.galleryImages = galleryImages; notifyDataSetChanged(); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockFrameLayout.java b/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockFrameLayout.java deleted file mode 100644 index 5b62bbfb..00000000 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockFrameLayout.java +++ /dev/null @@ -1,76 +0,0 @@ -package ml.docilealligator.infinityforreddit.customviews; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.FrameLayout; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class SwipeLockFrameLayout extends FrameLayout implements SwipeLockView { - @Nullable - private SwipeLockInterface swipeLockInterface = null; - private boolean locked = false; - - public SwipeLockFrameLayout(@NonNull Context context) { - super(context); - } - - public SwipeLockFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public SwipeLockFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public SwipeLockFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - @Override - public void setSwipeLockInterface(@Nullable SwipeLockInterface swipeLockInterface) { - this.swipeLockInterface = swipeLockInterface; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - updateSwipeLock(ev); - return locked; - } - - @SuppressLint("ClickableViewAccessibility") // we are just listening to touch events - @Override - public boolean onTouchEvent(MotionEvent ev) { - updateSwipeLock(ev); - return super.onTouchEvent(ev); - } - - /** - * Unlocks swipe if the view cannot be scrolled right anymore or if {@code ev} is - * {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL} - */ - private void updateSwipeLock(MotionEvent ev) { - if (swipeLockInterface != null) { - int action = ev.getAction(); - if (action == MotionEvent.ACTION_UP || - action == MotionEvent.ACTION_CANCEL) { - // calling SlidrInterface#unlock aborts the swipe - // so don't call unlock if it is already unlocked - if (locked) { - swipeLockInterface.unlockSwipe(); - locked = false; - } - } else { - if (!locked) { - swipeLockInterface.lockSwipe(); - locked = true; - } - } - - swipeLockInterface.setSwipeLocked(locked); - } - } -} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockLinearLayout.java b/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockLinearLayout.java new file mode 100644 index 00000000..85c95e63 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/customviews/SwipeLockLinearLayout.java @@ -0,0 +1,76 @@ +package ml.docilealligator.infinityforreddit.customviews; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public class SwipeLockLinearLayout extends LinearLayout implements SwipeLockView { + @Nullable + private SwipeLockInterface swipeLockInterface = null; + private boolean locked = false; + + public SwipeLockLinearLayout(@NonNull Context context) { + super(context); + } + + public SwipeLockLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public SwipeLockLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public SwipeLockLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public void setSwipeLockInterface(@Nullable SwipeLockInterface swipeLockInterface) { + this.swipeLockInterface = swipeLockInterface; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + updateSwipeLock(ev); + return locked; + } + + @SuppressLint("ClickableViewAccessibility") // we are just listening to touch events + @Override + public boolean onTouchEvent(MotionEvent ev) { + updateSwipeLock(ev); + return super.onTouchEvent(ev); + } + + /** + * Unlocks swipe if the view cannot be scrolled right anymore or if {@code ev} is + * {@link MotionEvent#ACTION_UP} or {@link MotionEvent#ACTION_CANCEL} + */ + private void updateSwipeLock(MotionEvent ev) { + if (swipeLockInterface != null) { + int action = ev.getAction(); + if (action == MotionEvent.ACTION_UP || + action == MotionEvent.ACTION_CANCEL) { + // calling SlidrInterface#unlock aborts the swipe + // so don't call unlock if it is already unlocked + if (locked) { + swipeLockInterface.unlockSwipe(); + locked = false; + } + } else { + if (!locked) { + swipeLockInterface.lockSwipe(); + locked = true; + } + } + + swipeLockInterface.setSwipeLocked(locked); + } + } +} -- cgit v1.2.3