From 5d7c37262e1c71af97b84212a977d60110c369e6 Mon Sep 17 00:00:00 2001 From: Grishka Date: Mon, 4 Dec 2023 21:33:25 +0300 Subject: [PATCH] Info sheet in media viewer (AND-109) --- mastodon/build.gradle | 4 +- .../fragments/BaseStatusListFragment.java | 3 +- .../ComposeImageDescriptionFragment.java | 18 +- .../android/fragments/ProfileFragment.java | 5 +- .../android/ui/PreReplySheet.java | 4 - .../MediaGridStatusDisplayItem.java | 130 +------------ .../android/ui/photoviewer/AltTextSheet.java | 37 ++++ .../android/ui/photoviewer/PhotoViewer.java | 126 +++++++++++- .../ui/photoviewer/PhotoViewerInfoSheet.java | 180 ++++++++++++++++++ .../android/ui/utils/UiUtils.java | 16 ++ .../src/main/res/drawable/bg_bottom_sheet.xml | 12 +- .../main/res/drawable/bg_button_m3_filled.xml | 4 +- .../res/drawable/bg_button_m3_filled_icon.xml | 3 + .../res/drawable/bg_button_m3_icon_label.xml | 9 + .../main/res/drawable/bg_button_m3_tonal.xml | 4 +- .../res/drawable/bg_button_m3_tonal_icon.xml | 3 + .../drawable/ic_bookmark_24px_selector.xml | 5 + .../res/drawable/ic_bookmark_fill1_24px.xml | 9 + .../main/res/drawable/ic_info_fill1_24px.xml | 9 + .../res/drawable/ic_star_24px_selector.xml | 5 + .../res/layout/overlay_image_alt_text.xml | 63 ------ .../src/main/res/layout/sheet_alt_text.xml | 63 ++++++ .../res/layout/sheet_photo_viewer_info.xml | 148 ++++++++++++++ .../src/main/res/layout/sheet_pre_reply.xml | 4 +- mastodon/src/main/res/values/strings.xml | 4 + mastodon/src/main/res/values/styles.xml | 12 ++ 26 files changed, 645 insertions(+), 235 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/AltTextSheet.java create mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewerInfoSheet.java create mode 100644 mastodon/src/main/res/drawable/bg_button_m3_filled_icon.xml create mode 100644 mastodon/src/main/res/drawable/bg_button_m3_icon_label.xml create mode 100644 mastodon/src/main/res/drawable/bg_button_m3_tonal_icon.xml create mode 100644 mastodon/src/main/res/drawable/ic_bookmark_24px_selector.xml create mode 100644 mastodon/src/main/res/drawable/ic_bookmark_fill1_24px.xml create mode 100644 mastodon/src/main/res/drawable/ic_info_fill1_24px.xml create mode 100644 mastodon/src/main/res/drawable/ic_star_24px_selector.xml delete mode 100644 mastodon/src/main/res/layout/overlay_image_alt_text.xml create mode 100644 mastodon/src/main/res/layout/sheet_alt_text.xml create mode 100644 mastodon/src/main/res/layout/sheet_photo_viewer_info.xml diff --git a/mastodon/build.gradle b/mastodon/build.gradle index 9c94c26e2..c37669dad 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -9,7 +9,7 @@ android { applicationId "org.joinmastodon.android" minSdk 23 targetSdk 33 - versionCode 80 + versionCode 81 versionName "2.2.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "da-rDK", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fa-rIR", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "ig-rNG", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "my-rMM", "nl-rNL", "no-rNO", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "ur-rIN", "vi-rVN", "zh-rCN", "zh-rTW" @@ -76,7 +76,7 @@ dependencies { implementation 'me.grishka.litex:viewpager:1.0.0' implementation 'me.grishka.litex:viewpager2:1.0.0' implementation 'me.grishka.litex:palette:1.0.0' - implementation 'me.grishka.appkit:appkit:1.2.15' + implementation 'me.grishka.appkit:appkit:1.2.16' implementation 'com.google.code.gson:gson:2.8.9' implementation 'org.jsoup:jsoup:1.14.3' implementation 'com.squareup:otto:1.3.8' 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 adb69fac4..cc2a9111b 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -32,7 +32,6 @@ import org.joinmastodon.android.ui.M3AlertDialogBuilder; import org.joinmastodon.android.ui.NonMutualPreReplySheet; import org.joinmastodon.android.ui.OldPostPreReplySheet; import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem; -import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem; @@ -182,7 +181,7 @@ public abstract class BaseStatusListFragment exten @Override public void openPhotoViewer(String parentID, Status _status, int attachmentIndex, MediaGridStatusDisplayItem.Holder gridHolder){ final Status status=_status.getContentStatus(); - currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){ + currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, status, accountID, new PhotoViewer.Listener(){ private MediaAttachmentViewController transitioningHolder; @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java index 812d6fab6..5e3cc22c3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeImageDescriptionFragment.java @@ -7,10 +7,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.MediaMetadataRetriever; import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.text.SpannableStringBuilder; -import android.text.style.BulletSpan; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -131,20 +128,9 @@ public class ComposeImageDescriptionFragment extends MastodonToolbarFragment imp @Override public boolean onOptionsItemSelected(MenuItem item){ if(item.getItemId()==R.id.help){ - SpannableStringBuilder msg=new SpannableStringBuilder(getText(R.string.alt_text_help)); - BulletSpan[] spans=msg.getSpans(0, msg.length(), BulletSpan.class); - for(BulletSpan span:spans){ - BulletSpan betterSpan; - if(Build.VERSION.SDK_INTcurrentPhotoViewer=null, ()->ava, null, null)); + null, accountID, new SingleImagePhotoViewerListener(avatar, avatarBorder, new int[]{radius, radius, radius, radius}, this, ()->currentPhotoViewer=null, ()->ava, null, null)); } } @@ -989,7 +988,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList if(drawable==null || drawable instanceof ColorDrawable) return; currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.header, drawable), 0, - new SingleImagePhotoViewerListener(cover, cover, null, this, ()->currentPhotoViewer=null, ()->drawable, ()->avatarBorder.setTranslationZ(2), ()->avatarBorder.setTranslationZ(0))); + null, accountID, new SingleImagePhotoViewerListener(cover, cover, null, this, ()->currentPhotoViewer=null, ()->drawable, ()->avatarBorder.setTranslationZ(2), ()->avatarBorder.setTranslationZ(0))); } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/PreReplySheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/PreReplySheet.java index 208076567..2fb3c9a7a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/PreReplySheet.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/PreReplySheet.java @@ -5,7 +5,6 @@ import android.graphics.drawable.ColorDrawable; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; @@ -14,7 +13,6 @@ import org.joinmastodon.android.R; import org.joinmastodon.android.ui.utils.UiUtils; import androidx.annotation.NonNull; -import me.grishka.appkit.utils.V; import me.grishka.appkit.views.BottomSheet; public abstract class PreReplySheet extends BottomSheet{ @@ -28,8 +26,6 @@ public abstract class PreReplySheet extends BottomSheet{ View content=context.getSystemService(LayoutInflater.class).inflate(R.layout.sheet_pre_reply, null); setContentView(content); - FrameLayout.LayoutParams lp=(FrameLayout.LayoutParams) content.getLayoutParams(); - lp.topMargin=V.dp(72); setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.colorM3Surface), UiUtils.getThemeColor(context, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme()); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java index c610bba5b..e21d557e1 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/MediaGridStatusDisplayItem.java @@ -26,6 +26,7 @@ import org.joinmastodon.android.model.Translation; import org.joinmastodon.android.ui.OutlineProviders; import org.joinmastodon.android.ui.PhotoLayoutHelper; import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable; +import org.joinmastodon.android.ui.photoviewer.AltTextSheet; import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost; import org.joinmastodon.android.ui.utils.MediaAttachmentViewController; import org.joinmastodon.android.ui.views.FrameLayoutThatOnlyMeasuresFirstChild; @@ -103,11 +104,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ 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; @@ -115,9 +111,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ private final TextView hideSensitiveButton; private final TextView sensitiveText; - private int altTextIndex=-1; - private Animator altTextAnimator; - public Holder(Activity activity, ViewGroup parent){ super(new FrameLayoutThatOnlyMeasuresFirstChild(activity)); wrapper=(FrameLayout)itemView; @@ -129,14 +122,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ 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); - hideSensitiveButton=(TextView) activity.getLayoutInflater().inflate(R.layout.alt_text_badge, overlays, false); hideSensitiveButton.setText(R.string.hide); FrameLayout.LayoutParams lp; @@ -160,9 +145,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ public void onBind(MediaGridStatusDisplayItem item){ wrapper.setPadding(0, 0, 0, item.inset ? 0 : V.dp(8)); - if(altTextAnimator!=null) - altTextAnimator.cancel(); - layout.setTiledLayout(item.tiledLayout); for(MediaAttachmentViewController c:controllers){ item.viewPool.reuse(c.type, c); @@ -212,8 +194,6 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ c.bind(att, item.status); i++; } - altTextWrapper.setVisibility(View.GONE); - altTextIndex=-1; if(!item.sensitiveRevealed){ sensitiveOverlay.setVisibility(View.VISIBLE); @@ -246,115 +226,9 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{ } private void onAltTextClick(View v){ - if(altTextAnimator!=null) - altTextAnimator.cancel(); - v.setVisibility(View.INVISIBLE); int index=(Integer)v.getTag(); - altTextIndex=index; Attachment att=item.attachments.get(index); - altText.setText(att.description); - altTextWrapper.setVisibility(View.VISIBLE); - altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){ - @Override - public boolean onPreDraw(){ - altTextWrapper.getViewTreeObserver().removeOnPreDrawListener(this); - - int[] loc={0, 0}; - v.getLocationInWindow(loc); - int btnL=loc[0], btnT=loc[1]; - overlays.getLocationInWindow(loc); - btnL-=loc[0]; - btnT-=loc[1]; - - ArrayList anims=new ArrayList<>(); - anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1, 0)); - anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0, 1)); - anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0, 1)); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL, altTextWrapper.getLeft())); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT, altTextWrapper.getTop())); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+v.getWidth(), altTextWrapper.getRight())); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+v.getHeight(), altTextWrapper.getBottom())); - for(Animator a:anims) - a.setDuration(300); - - for(MediaAttachmentViewController c:controllers){ - if(c.altButton!=null && c.altButton!=v){ - anims.add(ObjectAnimator.ofFloat(c.altButton, View.ALPHA, 1, 0).setDuration(150)); - } - } - - AnimatorSet set=new AnimatorSet(); - set.playTogether(anims); - set.setInterpolator(CubicBezierInterpolator.DEFAULT); - set.addListener(new AnimatorListenerAdapter(){ - @Override - public void onAnimationEnd(Animator animation){ - altTextAnimator=null; - for(MediaAttachmentViewController c:controllers){ - if(c.altButton!=null){ - c.altButton.setVisibility(View.INVISIBLE); - } - } - } - }); - altTextAnimator=set; - set.start(); - - return true; - } - }); - } - - private void onAltTextCloseClick(View v){ - if(altTextAnimator!=null) - altTextAnimator.cancel(); - - View btn=controllers.get(altTextIndex).altButton; - int i=0; - for(MediaAttachmentViewController c:controllers){ - if(c.altButton!=null && c.altButton!=btn && !TextUtils.isEmpty(item.attachments.get(i).description)) - c.altButton.setVisibility(View.VISIBLE); - i++; - } - - int[] loc={0, 0}; - btn.getLocationInWindow(loc); - int btnL=loc[0], btnT=loc[1]; - overlays.getLocationInWindow(loc); - btnL-=loc[0]; - btnT-=loc[1]; - - ArrayList anims=new ArrayList<>(); - anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1)); - anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0)); - anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0)); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL)); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT)); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+btn.getWidth())); - anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+btn.getHeight())); - for(Animator a:anims) - a.setDuration(300); - - for(MediaAttachmentViewController c:controllers){ - if(c.altButton!=null && c.altButton!=btn){ - anims.add(ObjectAnimator.ofFloat(c.altButton, View.ALPHA, 1).setDuration(150)); - } - } - - AnimatorSet set=new AnimatorSet(); - set.playTogether(anims); - set.setInterpolator(CubicBezierInterpolator.DEFAULT); - set.addListener(new AnimatorListenerAdapter(){ - @Override - public void onAnimationEnd(Animator animation){ - altTextAnimator=null; - altTextWrapper.setVisibility(View.GONE); - btn.setVisibility(View.VISIBLE); - btn.setAlpha(1); - } - }); - altTextAnimator=set; - set.start(); + new AltTextSheet(v.getContext(), att).show(); } public MediaAttachmentViewController getViewController(int index){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/AltTextSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/AltTextSheet.java new file mode 100644 index 000000000..b4affaf3c --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/AltTextSheet.java @@ -0,0 +1,37 @@ +package org.joinmastodon.android.ui.photoviewer; + +import android.content.Context; +import android.graphics.drawable.ColorDrawable; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.model.Attachment; +import org.joinmastodon.android.ui.M3AlertDialogBuilder; +import org.joinmastodon.android.ui.utils.UiUtils; + +import androidx.annotation.NonNull; +import me.grishka.appkit.views.BottomSheet; + +public class AltTextSheet extends BottomSheet{ + public AltTextSheet(@NonNull Context context, Attachment attachment){ + super(context); + + View content=context.getSystemService(LayoutInflater.class).inflate(R.layout.sheet_alt_text, null); + setContentView(content); + TextView altText=findViewById(R.id.alt_text); + altText.setText(attachment.description); + findViewById(R.id.alt_text_help).setOnClickListener(v->showAltTextHelp()); + setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.colorM3Surface), + UiUtils.getThemeColor(context, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme()); + } + + private void showAltTextHelp(){ + new M3AlertDialogBuilder(getContext()) + .setTitle(R.string.what_is_alt_text) + .setMessage(UiUtils.fixBulletListInString(getContext(), R.string.alt_text_help)) + .setPositiveButton(R.string.ok, null) + .show(); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java index 9266d28d1..a59e2691e 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java @@ -1,6 +1,10 @@ package org.joinmastodon.android.ui.photoviewer; import android.Manifest; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; import android.app.DownloadManager; @@ -26,6 +30,8 @@ import android.os.SystemClock; import android.provider.MediaStore; import android.provider.Settings; import android.util.Log; +import android.util.Property; +import android.view.ContextThemeWrapper; import android.view.DisplayCutout; import android.view.Gravity; import android.view.KeyEvent; @@ -48,8 +54,12 @@ import android.widget.Toolbar; import org.joinmastodon.android.R; import org.joinmastodon.android.api.MastodonAPIController; +import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.StatusCountersUpdatedEvent; import org.joinmastodon.android.model.Attachment; +import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.M3AlertDialogBuilder; +import org.joinmastodon.android.ui.utils.UiUtils; import java.io.File; import java.io.FileOutputStream; @@ -85,6 +95,8 @@ public class PhotoViewer implements ZoomPanView.Listener{ private int currentIndex; private WindowManager wm; private Listener listener; + private Status status; + private String accountID; private FrameLayout windowView; private FragmentRootLinearLayout uiOverlay; @@ -104,17 +116,32 @@ public class PhotoViewer implements ZoomPanView.Listener{ if(uiVisible) toggleUI(); }; + private Animator currentSheetRelatedToolbarAnimation; private boolean videoPositionNeedsUpdating; private Runnable videoPositionUpdater=this::updateVideoPosition; private int videoDuration, videoInitialPosition, videoLastTimeUpdatePosition; private long videoInitialPositionTime; - public PhotoViewer(Activity activity, List attachments, int index, Listener listener){ + private static final Property STATUS_BAR_COLOR_PROPERTY=new Property<>(Integer.class, "Fdsafdsa"){ + @Override + public Integer get(FragmentRootLinearLayout object){ + return object.getStatusBarColor(); + } + + @Override + public void set(FragmentRootLinearLayout object, Integer value){ + object.setStatusBarColor(value); + } + }; + + public PhotoViewer(Activity activity, List attachments, int index, Status status, String accountID, Listener listener){ this.activity=activity; this.attachments=attachments.stream().filter(a->a.type==Attachment.Type.IMAGE || a.type==Attachment.Type.GIFV || a.type==Attachment.Type.VIDEO).collect(Collectors.toList()); currentIndex=index; this.listener=listener; + this.status=status; + this.accountID=accountID; wm=activity.getWindowManager(); @@ -175,9 +202,15 @@ public class PhotoViewer implements ZoomPanView.Listener{ toolbarWrap=uiOverlay.findViewById(R.id.toolbar_wrap); toolbar=uiOverlay.findViewById(R.id.toolbar); toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0)); - toolbar.getMenu().add(R.string.download).setIcon(R.drawable.ic_download_24px).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + if(status!=null) + toolbar.getMenu().add(R.string.info).setIcon(R.drawable.ic_info_24px).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + else + toolbar.getMenu().add(R.string.download).setIcon(R.drawable.ic_download_24px).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); toolbar.setOnMenuItemClickListener(item->{ - saveCurrentFile(); + if(status!=null) + showInfoSheet(); + else + saveCurrentFile(); return true; }); uiOverlay.setAlpha(0f); @@ -610,6 +643,93 @@ public class PhotoViewer implements ZoomPanView.Listener{ } } + private void showInfoSheet(){ + pauseVideo(); + PhotoViewerInfoSheet sheet=new PhotoViewerInfoSheet(new ContextThemeWrapper(activity, R.style.Theme_Mastodon_Dark), attachments.get(currentIndex), toolbar.getHeight(), new PhotoViewerInfoSheet.Listener(){ + private boolean ignoreBeforeDismiss; + + @Override + public void onBeforeDismiss(int duration){ + if(ignoreBeforeDismiss) + return; + if(currentSheetRelatedToolbarAnimation!=null) + currentSheetRelatedToolbarAnimation.cancel(); + AnimatorSet set=new AnimatorSet(); + set.playTogether( + ObjectAnimator.ofFloat(pager, View.TRANSLATION_Y, 0), + ObjectAnimator.ofFloat(toolbarWrap, View.ALPHA, 1f), + ObjectAnimator.ofArgb(uiOverlay, STATUS_BAR_COLOR_PROPERTY, 0x80000000) + ); + set.setDuration(duration); + set.setInterpolator(CubicBezierInterpolator.EASE_OUT); + currentSheetRelatedToolbarAnimation=set; + set.addListener(new AnimatorListenerAdapter(){ + @Override + public void onAnimationEnd(Animator animation){ + currentSheetRelatedToolbarAnimation=null; + } + }); + set.start(); + } + + @Override + public void onDismissEntireViewer(){ + ignoreBeforeDismiss=true; + onStartSwipeToDismissTransition(0); + } + + @Override + public void onButtonClick(int id){ + if(id==R.id.btn_boost){ + if(status!=null){ + AccountSessionManager.get(accountID).getStatusInteractionController().setReblogged(status, !status.reblogged); + } + }else if(id==R.id.btn_favorite){ + if(status!=null){ + AccountSessionManager.get(accountID).getStatusInteractionController().setFavorited(status, !status.favourited); + } + }else if(id==R.id.btn_share){ + if(status!=null){ + UiUtils.openSystemShareSheet(activity, status.url); + } + }else if(id==R.id.btn_bookmark){ + if(status!=null){ + AccountSessionManager.get(accountID).getStatusInteractionController().setBookmarked(status, !status.bookmarked); + } + }else if(id==R.id.btn_download){ + saveCurrentFile(); + } + } + }); + sheet.setStatus(status); + sheet.show(); + if(currentSheetRelatedToolbarAnimation!=null) + currentSheetRelatedToolbarAnimation.cancel(); + sheet.getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){ + @Override + public boolean onPreDraw(){ + sheet.getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this); + AnimatorSet set=new AnimatorSet(); + set.playTogether( + ObjectAnimator.ofFloat(pager, View.TRANSLATION_Y, -pager.getHeight()*0.2f), + ObjectAnimator.ofFloat(toolbarWrap, View.ALPHA, 0f), + ObjectAnimator.ofArgb(uiOverlay, STATUS_BAR_COLOR_PROPERTY, 0) + ); + set.setDuration(300); + set.setInterpolator(CubicBezierInterpolator.DEFAULT); + currentSheetRelatedToolbarAnimation=set; + set.addListener(new AnimatorListenerAdapter(){ + @Override + public void onAnimationEnd(Animator animation){ + currentSheetRelatedToolbarAnimation=null; + } + }); + set.start(); + return true; + } + }); + } + public interface Listener{ void setPhotoViewVisibility(int index, boolean visible); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewerInfoSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewerInfoSheet.java new file mode 100644 index 000000000..ac8f05005 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewerInfoSheet.java @@ -0,0 +1,180 @@ +package org.joinmastodon.android.ui.photoviewer; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.ColorDrawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.joinmastodon.android.E; +import org.joinmastodon.android.R; +import org.joinmastodon.android.events.StatusCountersUpdatedEvent; +import org.joinmastodon.android.model.Attachment; +import org.joinmastodon.android.model.Status; +import org.joinmastodon.android.model.StatusPrivacy; +import org.joinmastodon.android.ui.M3AlertDialogBuilder; +import org.joinmastodon.android.ui.utils.UiUtils; + +import androidx.annotation.NonNull; +import me.grishka.appkit.utils.CubicBezierInterpolator; +import me.grishka.appkit.utils.V; +import me.grishka.appkit.views.BottomSheet; + +public class PhotoViewerInfoSheet extends BottomSheet{ + private final Attachment attachment; + private final View buttonsContainer; + private final TextView altText; + private final ImageButton backButton, infoButton; + private final Button boostBtn, favoriteBtn, bookmarkBtn; + private final Listener listener; + private String statusID; + + public PhotoViewerInfoSheet(@NonNull Context context, Attachment attachment, int toolbarHeight, Listener listener){ + super(context); + this.attachment=attachment; + this.listener=listener; + + dimAmount=0; + View content=context.getSystemService(LayoutInflater.class).inflate(R.layout.sheet_photo_viewer_info, null); + setContentView(content); + setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(context, R.attr.colorM3Surface), + UiUtils.getThemeColor(context, R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme()); + + buttonsContainer=findViewById(R.id.buttons_container); + altText=findViewById(R.id.alt_text); + + if(TextUtils.isEmpty(attachment.description)){ + findViewById(R.id.alt_text).setVisibility(View.GONE); + findViewById(R.id.alt_text_title).setVisibility(View.GONE); + findViewById(R.id.divider).setVisibility(View.GONE); + }else{ + altText.setText(attachment.description); + findViewById(R.id.alt_text_help).setOnClickListener(v->showAltTextHelp()); + } + + backButton=new ImageButton(context); + backButton.setImageResource(R.drawable.ic_arrow_back); + backButton.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(context, R.attr.colorM3OnSurfaceVariant))); + backButton.setBackgroundResource(R.drawable.bg_button_m3_tonal_icon); + backButton.setOutlineProvider(ViewOutlineProvider.BACKGROUND); + backButton.setElevation(V.dp(2)); + backButton.setAlpha(0f); + backButton.setOnClickListener(v->{ + listener.onDismissEntireViewer(); + dismiss(); + }); + + infoButton=new ImageButton(context); + infoButton.setImageResource(R.drawable.ic_info_fill1_24px); + infoButton.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(context, R.attr.colorM3OnPrimary))); + infoButton.setBackgroundResource(R.drawable.bg_button_m3_filled_icon); + infoButton.setOutlineProvider(ViewOutlineProvider.BACKGROUND); + infoButton.setElevation(V.dp(2)); + infoButton.setAlpha(0f); + infoButton.setSelected(true); + infoButton.setOnClickListener(v->dismiss()); + + FrameLayout.LayoutParams lp=new FrameLayout.LayoutParams(V.dp(48), V.dp(48)); + lp.topMargin=toolbarHeight/2-V.dp(24); + lp.leftMargin=lp.rightMargin=V.dp(4); + lp.gravity=Gravity.START | Gravity.TOP; + container.addView(backButton, lp); + + lp=new FrameLayout.LayoutParams(lp); + lp.leftMargin=lp.rightMargin=0; + lp.gravity=Gravity.END | Gravity.TOP; + container.addView(infoButton, lp); + + boostBtn=findViewById(R.id.btn_boost); + favoriteBtn=findViewById(R.id.btn_favorite); + bookmarkBtn=findViewById(R.id.btn_bookmark); + View.OnClickListener clickListener=v->listener.onButtonClick(v.getId()); + + boostBtn.setOnClickListener(clickListener); + favoriteBtn.setOnClickListener(clickListener); + findViewById(R.id.btn_share).setOnClickListener(clickListener); + bookmarkBtn.setOnClickListener(clickListener); + findViewById(R.id.btn_download).setOnClickListener(clickListener); + } + + private void showAltTextHelp(){ + new M3AlertDialogBuilder(getContext()) + .setTitle(R.string.what_is_alt_text) + .setMessage(UiUtils.fixBulletListInString(getContext(), R.string.alt_text_help)) + .setPositiveButton(R.string.ok, null) + .show(); + } + + @Override + public void dismiss(){ + if(dismissed) + return; + int height=content.getHeight(); + int duration=Math.max(60, (int) (180 * (height - content.getTranslationY()) / (float) height)); + listener.onBeforeDismiss(duration); + backButton.animate().alpha(0).setDuration(duration).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + infoButton.animate().alpha(0).setDuration(duration).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + super.dismiss(); + E.unregister(this); + } + + @Override + public void show(){ + super.show(); + E.register(this); + content.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){ + @Override + public boolean onPreDraw(){ + content.getViewTreeObserver().removeOnPreDrawListener(this); + backButton.animate().alpha(1).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + infoButton.animate().alpha(1).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + return true; + } + }); + } + + public void setStatus(Status status){ + statusID=status.id; + boostBtn.setCompoundDrawablesWithIntrinsicBounds(0, switch(status.visibility){ + case DIRECT -> R.drawable.ic_boost_disabled_24px; + case PUBLIC, UNLISTED -> R.drawable.ic_boost; + case PRIVATE -> R.drawable.ic_boost_private; + }, 0, 0); + boostBtn.setEnabled(status.visibility!=StatusPrivacy.DIRECT); + setButtonStates(status.reblogged, status.favourited, status.bookmarked); + } + + @Subscribe + public void onCountersUpdated(StatusCountersUpdatedEvent ev){ + if(ev.id.equals(statusID)){ + setButtonStates(ev.reblogged, ev.favorited, ev.bookmarked); + } + } + + private void setButtonStates(boolean reblogged, boolean favorited, boolean bookmarked){ + boostBtn.setText(reblogged ? R.string.button_reblogged : R.string.button_reblog); + boostBtn.setSelected(reblogged); + + favoriteBtn.setText(favorited ? R.string.button_favorited : R.string.button_favorite); + favoriteBtn.setSelected(favorited); + + bookmarkBtn.setText(bookmarked ? R.string.bookmarked : R.string.add_bookmark); + bookmarkBtn.setSelected(bookmarked); + } + + public interface Listener{ + void onBeforeDismiss(int duration); + void onDismissEntireViewer(); + void onButtonClick(int id); + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java index 599378d5f..b946e64bf 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/utils/UiUtils.java @@ -26,6 +26,7 @@ import android.provider.OpenableColumns; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; +import android.text.style.BulletSpan; import android.transition.ChangeBounds; import android.transition.ChangeScroll; import android.transition.Fade; @@ -866,4 +867,19 @@ public class UiUtils{ lp.setMarginEnd(V.dp(marginEnd)); return lp; } + + public static CharSequence fixBulletListInString(Context context, @StringRes int res){ + SpannableStringBuilder msg=new SpannableStringBuilder(context.getText(res)); + BulletSpan[] spans=msg.getSpans(0, msg.length(), BulletSpan.class); + for(BulletSpan span:spans){ + BulletSpan betterSpan; + if(Build.VERSION.SDK_INT - - + + - - - - - - - + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_button_m3_filled.xml b/mastodon/src/main/res/drawable/bg_button_m3_filled.xml index 8ba6277c9..f9b427ead 100644 --- a/mastodon/src/main/res/drawable/bg_button_m3_filled.xml +++ b/mastodon/src/main/res/drawable/bg_button_m3_filled.xml @@ -5,7 +5,7 @@ - + @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_button_m3_filled_icon.xml b/mastodon/src/main/res/drawable/bg_button_m3_filled_icon.xml new file mode 100644 index 000000000..2c4ea8a12 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_button_m3_filled_icon.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_button_m3_icon_label.xml b/mastodon/src/main/res/drawable/bg_button_m3_icon_label.xml new file mode 100644 index 000000000..73eec74d5 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_button_m3_icon_label.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_button_m3_tonal.xml b/mastodon/src/main/res/drawable/bg_button_m3_tonal.xml index d6043fc37..f5885b936 100644 --- a/mastodon/src/main/res/drawable/bg_button_m3_tonal.xml +++ b/mastodon/src/main/res/drawable/bg_button_m3_tonal.xml @@ -5,7 +5,7 @@ - + @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/bg_button_m3_tonal_icon.xml b/mastodon/src/main/res/drawable/bg_button_m3_tonal_icon.xml new file mode 100644 index 000000000..81a9482a9 --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_button_m3_tonal_icon.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_bookmark_24px_selector.xml b/mastodon/src/main/res/drawable/ic_bookmark_24px_selector.xml new file mode 100644 index 000000000..66792647d --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_bookmark_24px_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/drawable/ic_bookmark_fill1_24px.xml b/mastodon/src/main/res/drawable/ic_bookmark_fill1_24px.xml new file mode 100644 index 000000000..2b286bbc1 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_bookmark_fill1_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_info_fill1_24px.xml b/mastodon/src/main/res/drawable/ic_info_fill1_24px.xml new file mode 100644 index 000000000..077f39342 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_info_fill1_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/drawable/ic_star_24px_selector.xml b/mastodon/src/main/res/drawable/ic_star_24px_selector.xml new file mode 100644 index 000000000..1b4090e75 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_star_24px_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/overlay_image_alt_text.xml b/mastodon/src/main/res/layout/overlay_image_alt_text.xml deleted file mode 100644 index c0bba80e9..000000000 --- a/mastodon/src/main/res/layout/overlay_image_alt_text.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/mastodon/src/main/res/layout/sheet_alt_text.xml b/mastodon/src/main/res/layout/sheet_alt_text.xml new file mode 100644 index 000000000..4c6837fd0 --- /dev/null +++ b/mastodon/src/main/res/layout/sheet_alt_text.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mastodon/src/main/res/layout/sheet_photo_viewer_info.xml b/mastodon/src/main/res/layout/sheet_photo_viewer_info.xml new file mode 100644 index 000000000..b588e173e --- /dev/null +++ b/mastodon/src/main/res/layout/sheet_photo_viewer_info.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + +