From e873dd7d0abfb2ee4a1d82dc45c43441d6033ee8 Mon Sep 17 00:00:00 2001 From: Grishka Date: Fri, 24 Mar 2023 00:33:02 +0300 Subject: [PATCH] New sensitive media design --- .../fragments/AccountTimelineFragment.java | 1 + .../fragments/BaseStatusListFragment.java | 102 +----------------- .../MediaGridStatusDisplayItem.java | 76 +++++++++++-- .../ui/drawables/SpoilerStripesDrawable.java | 3 + .../android/ui/utils/BlurHashDrawable.java | 6 ++ .../utils/MediaAttachmentViewController.java | 8 +- .../main/res/drawable/bg_spoiler_overlay.xml | 25 +++++ .../res/drawable/ic_visibility_off_24px.xml | 9 ++ .../src/main/res/layout/alt_text_badge.xml | 18 ++++ .../main/res/layout/display_item_photo.xml | 18 +--- .../res/layout/overlay_image_sensitive.xml | 39 +++++++ mastodon/src/main/res/values/attrs.xml | 1 + mastodon/src/main/res/values/strings.xml | 5 +- mastodon/src/main/res/values/styles.xml | 6 +- 14 files changed, 180 insertions(+), 137 deletions(-) create mode 100644 mastodon/src/main/res/drawable/bg_spoiler_overlay.xml create mode 100644 mastodon/src/main/res/drawable/ic_visibility_off_24px.xml create mode 100644 mastodon/src/main/res/layout/alt_text_badge.xml create mode 100644 mastodon/src/main/res/layout/overlay_image_sensitive.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index 79aec1697..f2b50c3bd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -74,6 +74,7 @@ public class AccountTimelineFragment extends StatusListFragment{ @Override public void onViewCreated(View view, Bundle savedInstanceState){ super.onViewCreated(view, savedInstanceState); + view.setBackground(null); // prevents unnecessary overdraw } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index aa8567c76..8e447ed24 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -428,35 +428,6 @@ public abstract class BaseStatusListFragment exten } } - public void onVisibilityIconClick(HeaderStatusDisplayItem.Holder holder){ - Status status=holder.getItem().status; - status.spoilerRevealed=!status.spoilerRevealed; - if(!TextUtils.isEmpty(status.spoilerText)){ - TextStatusDisplayItem.Holder text=findHolderOfType(holder.getItemID(), TextStatusDisplayItem.Holder.class); - if(text!=null){ - adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()); - } - } - holder.rebind(); - updateImagesSpoilerState(status, holder.getItemID()); - } - - protected void updateImagesSpoilerState(Status status, String itemID){ - ArrayList updatedPositions=new ArrayList<>(); - MediaGridStatusDisplayItem.Holder mediaGrid=findHolderOfType(itemID, MediaGridStatusDisplayItem.Holder.class); - if(mediaGrid!=null){ - mediaGrid.setRevealed(status.spoilerRevealed); - updatedPositions.add(mediaGrid.getAbsoluteAdapterPosition()-getMainAdapterOffset()); - } - int i=0; - for(StatusDisplayItem item:displayItems){ - if(itemID.equals(item.parentID) && item instanceof MediaGridStatusDisplayItem && !updatedPositions.contains(i)){ - adapter.notifyItemChanged(i); - } - i++; - } - } - public void onGapClick(GapStatusDisplayItem.Holder item){} public String getAccountID(){ @@ -622,10 +593,7 @@ public abstract class BaseStatusListFragment exten } private class StatusListItemDecoration extends RecyclerView.ItemDecoration{ - private Paint dividerPaint=new Paint(), hiddenMediaPaint=new Paint(Paint.ANTI_ALIAS_FLAG); - private Typeface mediumTypeface=Typeface.create("sans-serif-medium", Typeface.NORMAL); - private Layout mediaHiddenTitleLayout, mediaHiddenTextLayout, tapToRevealTextLayout; - private int currentMediaHiddenLayoutsWidth=0; + private Paint dividerPaint=new Paint(); { dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Outline)); @@ -646,73 +614,5 @@ public abstract class BaseStatusListFragment exten } } } - - @Override - public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){ - for(int i=0;i attachments; private final ArrayList requests=new ArrayList<>(); public final Status status; + public boolean sensitiveRevealed; public MediaGridStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, PhotoLayoutHelper.TiledLayoutResult tiledLayout, List attachments, Status status){ super(parentID, parentFragment); @@ -49,6 +55,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ this.viewPool=parentFragment.getAttachmentViewsPool(); this.attachments=attachments; this.status=status; + sensitiveRevealed=!status.sensitive; for(Attachment att:attachments){ requests.add(new UrlImageLoaderRequest(switch(att.type){ case IMAGE -> att.url; @@ -85,12 +92,18 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ private final View.OnClickListener clickListener=this::onViewClick, altTextClickListener=this::onAltTextClick; private final ArrayList controllers=new ArrayList<>(); + private final MaxWidthFrameLayout overlays; private final FrameLayout altTextWrapper; private final TextView altTextButton; private final View altTextScroller; private final ImageButton altTextClose; private final TextView altText; + private final View sensitiveOverlay; + private final LayerDrawable sensitiveOverlayBG; + private static final ColorDrawable drawableForWhenThereIsNoBlurhash=new ColorDrawable(0xffffffff); + private final TextView hideSensitiveButton; + private int altTextIndex=-1; private Animator altTextAnimator; @@ -100,15 +113,34 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ layout=new MediaGridLayout(activity); wrapper.addView(layout); wrapper.setPadding(0, 0, 0, V.dp(8)); + wrapper.setClipToPadding(false); - activity.getLayoutInflater().inflate(R.layout.overlay_image_alt_text, wrapper); + overlays=new MaxWidthFrameLayout(activity); + overlays.setMaxWidth(V.dp(MediaGridLayout.MAX_WIDTH)); + wrapper.addView(overlays, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER_HORIZONTAL)); + + activity.getLayoutInflater().inflate(R.layout.overlay_image_alt_text, overlays); altTextWrapper=findViewById(R.id.alt_text_wrapper); altTextButton=findViewById(R.id.alt_button); altTextScroller=findViewById(R.id.alt_text_scroller); altTextClose=findViewById(R.id.alt_text_close); altText=findViewById(R.id.alt_text); altTextClose.setOnClickListener(this::onAltTextCloseClick); - wrapper.setClipToPadding(false); + + hideSensitiveButton=(TextView) activity.getLayoutInflater().inflate(R.layout.alt_text_badge, overlays, false); + hideSensitiveButton.setText(R.string.hide); + FrameLayout.LayoutParams lp; + overlays.addView(hideSensitiveButton, lp=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, V.dp(24), Gravity.END | Gravity.BOTTOM)); + int margin=V.dp(8); + lp.setMargins(margin, margin, margin, margin); + + activity.getLayoutInflater().inflate(R.layout.overlay_image_sensitive, overlays); + sensitiveOverlay=findViewById(R.id.sensitive_overlay); + sensitiveOverlayBG=(LayerDrawable) sensitiveOverlay.getBackground(); + sensitiveOverlayBG.setDrawableByLayerId(R.id.left_drawable, new SpoilerStripesDrawable(false)); + sensitiveOverlayBG.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable(true)); + sensitiveOverlay.setOnClickListener(v->revealSensitive()); + hideSensitiveButton.setOnClickListener(v->hideSensitive()); } @Override @@ -148,6 +180,16 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ } altTextWrapper.setVisibility(View.GONE); altTextIndex=-1; + + if(!item.sensitiveRevealed){ + sensitiveOverlay.setVisibility(View.VISIBLE); + layout.setVisibility(View.INVISIBLE); + updateBlurhashInSensitiveOverlay(); + }else{ + sensitiveOverlay.setVisibility(View.GONE); + layout.setVisibility(View.VISIBLE); + } + hideSensitiveButton.setVisibility(item.status.sensitive ? View.VISIBLE : View.GONE); } @Override @@ -182,7 +224,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ int[] loc={0, 0}; v.getLocationInWindow(loc); int btnL=loc[0], btnT=loc[1]; - wrapper.getLocationInWindow(loc); + overlays.getLocationInWindow(loc); btnL-=loc[0]; btnT-=loc[1]; @@ -240,7 +282,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ int[] loc={0, 0}; btn.getLocationInWindow(loc); int btnL=loc[0], btnT=loc[1]; - wrapper.getLocationInWindow(loc); + overlays.getLocationInWindow(loc); btnL-=loc[0]; btnT-=loc[1]; @@ -276,12 +318,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ set.start(); } - public void setRevealed(boolean revealed){ - for(MediaAttachmentViewController c:controllers){ - c.setRevealed(revealed); - } - } - public MediaAttachmentViewController getViewController(int index){ return controllers.get(index); } @@ -290,5 +326,25 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ layout.setClipChildren(clip); wrapper.setClipChildren(clip); } + + private void updateBlurhashInSensitiveOverlay(){ + Drawable d=item.attachments.get(0).blurhashPlaceholder; + sensitiveOverlayBG.setDrawableByLayerId(R.id.blurhash, d==null ? drawableForWhenThereIsNoBlurhash : d.mutate()); + } + + private void revealSensitive(){ + if(item.sensitiveRevealed) + return; + item.sensitiveRevealed=true; + V.setVisibilityAnimated(sensitiveOverlay, View.GONE); + layout.setVisibility(View.VISIBLE); + } + + private void hideSensitive(){ + if(!item.sensitiveRevealed) + return; + item.sensitiveRevealed=false; + V.setVisibilityAnimated(sensitiveOverlay, View.VISIBLE, ()->layout.setVisibility(View.INVISIBLE)); + } } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/drawables/SpoilerStripesDrawable.java b/mastodon/src/main/java/org/joinmastodon/android/ui/drawables/SpoilerStripesDrawable.java index f97dfd2dd..1a7aaad71 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/drawables/SpoilerStripesDrawable.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/drawables/SpoilerStripesDrawable.java @@ -11,6 +11,7 @@ import org.joinmastodon.android.MastodonApp; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import me.grishka.appkit.utils.V; public class SpoilerStripesDrawable extends Drawable{ private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG); @@ -33,6 +34,8 @@ public class SpoilerStripesDrawable extends Drawable{ canvas.translate(bounds.left, bounds.top); canvas.clipRect(0, 0, bounds.width(), bounds.height()); float scale=MastodonApp.context.getResources().getDisplayMetrics().density; + if(bounds.width()>V.dp(10)) + scale*=2; canvas.scale(scale, scale, 0, 0); float height=bounds.height()/scale; diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/BlurHashDrawable.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/BlurHashDrawable.java index 0763c1845..53e15e389 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/BlurHashDrawable.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/BlurHashDrawable.java @@ -50,4 +50,10 @@ public class BlurHashDrawable extends Drawable{ public int getIntrinsicHeight(){ return height; } + + @NonNull + @Override + public Drawable mutate(){ + return new BlurHashDrawable(bitmap, width, height); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/MediaAttachmentViewController.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/MediaAttachmentViewController.java index 3dbc75ec1..a2a0a1da9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/MediaAttachmentViewController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/MediaAttachmentViewController.java @@ -52,7 +52,7 @@ public class MediaAttachmentViewController{ this.status=status; crossfadeDrawable.setSize(attachment.getWidth(), attachment.getHeight()); crossfadeDrawable.setBlurhashDrawable(attachment.blurhashPlaceholder); - crossfadeDrawable.setCrossfadeAlpha(status.spoilerRevealed ? 0f : 1f); + crossfadeDrawable.setCrossfadeAlpha(0f); photo.setImageDrawable(null); photo.setImageDrawable(crossfadeDrawable); photo.setContentDescription(TextUtils.isEmpty(attachment.description) ? context.getString(R.string.media_no_description) : attachment.description); @@ -67,7 +67,7 @@ public class MediaAttachmentViewController{ public void setImage(Drawable drawable){ crossfadeDrawable.setImageDrawable(drawable); - if(didClear && status.spoilerRevealed) + if(didClear) crossfadeDrawable.animateAlpha(0f); } @@ -76,8 +76,4 @@ public class MediaAttachmentViewController{ crossfadeDrawable.setImageDrawable(null); didClear=true; } - - public void setRevealed(boolean revealed){ - crossfadeDrawable.animateAlpha(revealed ? 0f : 1f); - } } diff --git a/mastodon/src/main/res/drawable/bg_spoiler_overlay.xml b/mastodon/src/main/res/drawable/bg_spoiler_overlay.xml new file mode 100644 index 000000000..fb90b3e95 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_spoiler_overlay.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_visibility_off_24px.xml b/mastodon/src/main/res/drawable/ic_visibility_off_24px.xml new file mode 100644 index 000000000..cd9469b18 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_visibility_off_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/layout/alt_text_badge.xml b/mastodon/src/main/res/layout/alt_text_badge.xml new file mode 100644 index 000000000..fb92a1da9 --- /dev/null +++ b/mastodon/src/main/res/layout/alt_text_badge.xml @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/display_item_photo.xml b/mastodon/src/main/res/layout/display_item_photo.xml index 6655ec4ba..b06028979 100644 --- a/mastodon/src/main/res/layout/display_item_photo.xml +++ b/mastodon/src/main/res/layout/display_item_photo.xml @@ -12,21 +12,7 @@ android:scaleType="centerCrop"/> - + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/overlay_image_sensitive.xml b/mastodon/src/main/res/layout/overlay_image_sensitive.xml new file mode 100644 index 000000000..e214f300e --- /dev/null +++ b/mastodon/src/main/res/layout/overlay_image_sensitive.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/values/attrs.xml b/mastodon/src/main/res/values/attrs.xml index b3d0a39d2..9afc752b1 100644 --- a/mastodon/src/main/res/values/attrs.xml +++ b/mastodon/src/main/res/values/attrs.xml @@ -42,6 +42,7 @@ + diff --git a/mastodon/src/main/res/values/strings.xml b/mastodon/src/main/res/values/strings.xml index e2fb87d28..94c1cb0b7 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -268,8 +268,7 @@ Mastodon for Android v%1$s (%2$d) Media cache cleared Are you sure you want to sign out? - Sensitive content - The author marked this media as sensitive. Tap to reveal. + The author marked this media as sensitive. Tap to reveal Go to %s\'s profile More options @@ -448,4 +447,6 @@ View all Accounts Verified link + Show + Hide \ No newline at end of file diff --git a/mastodon/src/main/res/values/styles.xml b/mastodon/src/main/res/values/styles.xml index 6f7efd933..ed7344af6 100644 --- a/mastodon/src/main/res/values/styles.xml +++ b/mastodon/src/main/res/values/styles.xml @@ -71,7 +71,8 @@ ?colorM3Background @color/navigation_bar_bg_light ?colorM3Primary - @color/m3_primary_overlay + @color/m3_on_surface_overlay + #a6ffffff