Info sheet in media viewer (AND-109)
This commit is contained in:
@@ -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<T extends DisplayItemsParent> 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
|
||||
|
||||
@@ -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_INT<Build.VERSION_CODES.Q)
|
||||
betterSpan=new BulletSpan(V.dp(10), UiUtils.getThemeColor(themeWrapper, R.attr.colorM3OnSurface));
|
||||
else
|
||||
betterSpan=new BulletSpan(V.dp(10), UiUtils.getThemeColor(themeWrapper, R.attr.colorM3OnSurface), V.dp(1.5f));
|
||||
msg.setSpan(betterSpan, msg.getSpanStart(span), msg.getSpanEnd(span), msg.getSpanFlags(span));
|
||||
msg.removeSpan(span);
|
||||
}
|
||||
new M3AlertDialogBuilder(themeWrapper)
|
||||
.setTitle(R.string.what_is_alt_text)
|
||||
.setMessage(msg)
|
||||
.setMessage(UiUtils.fixBulletListInString(themeWrapper, R.string.alt_text_help))
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
@@ -181,7 +167,7 @@ public class ComposeImageDescriptionFragment extends MastodonToolbarFragment imp
|
||||
fakeAttachment.meta.width=width;
|
||||
fakeAttachment.meta.height=height;
|
||||
|
||||
photoViewer=new PhotoViewer(getActivity(), Collections.singletonList(fakeAttachment), 0, new PhotoViewer.Listener(){
|
||||
photoViewer=new PhotoViewer(getActivity(), Collections.singletonList(fakeAttachment), 0, null, accountID, new PhotoViewer.Listener(){
|
||||
@Override
|
||||
public void setPhotoViewVisibility(int index, boolean visible){
|
||||
image.setAlpha(visible ? 1f : 0f);
|
||||
|
||||
@@ -40,7 +40,6 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
@@ -977,7 +976,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
return;
|
||||
int radius=V.dp(25);
|
||||
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.avatar, ava), 0,
|
||||
new SingleImagePhotoViewerListener(avatar, avatarBorder, new int[]{radius, radius, radius, radius}, this, ()->currentPhotoViewer=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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<MediaAttachmentViewController> 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<Animator> 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<Animator> 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){
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<Attachment> attachments, int index, Listener listener){
|
||||
private static final Property<FragmentRootLinearLayout, Integer> 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<Attachment> 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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<Build.VERSION_CODES.Q)
|
||||
betterSpan=new BulletSpan(V.dp(10), UiUtils.getThemeColor(context, R.attr.colorM3OnSurface));
|
||||
else
|
||||
betterSpan=new BulletSpan(V.dp(10), UiUtils.getThemeColor(context, R.attr.colorM3OnSurface), V.dp(1.5f));
|
||||
msg.setSpan(betterSpan, msg.getSpanStart(span), msg.getSpanEnd(span), msg.getSpanFlags(span));
|
||||
msg.removeSpan(span);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user