Media viewer redesign (AND-196)
This commit is contained in:
@@ -186,7 +186,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
@Override
|
@Override
|
||||||
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex, MediaGridStatusDisplayItem.Holder gridHolder){
|
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex, MediaGridStatusDisplayItem.Holder gridHolder){
|
||||||
final Status status=_status.getContentStatus();
|
final Status status=_status.getContentStatus();
|
||||||
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, status, accountID, new PhotoViewer.Listener(){
|
currentPhotoViewer=new PhotoViewer(getActivity(), this, status.mediaAttachments, attachmentIndex, status, accountID, new PhotoViewer.Listener(){
|
||||||
private MediaAttachmentViewController transitioningHolder;
|
private MediaAttachmentViewController transitioningHolder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ public class ComposeImageDescriptionFragment extends MastodonToolbarFragment{
|
|||||||
fakeAttachment.meta.width=width;
|
fakeAttachment.meta.width=width;
|
||||||
fakeAttachment.meta.height=height;
|
fakeAttachment.meta.height=height;
|
||||||
|
|
||||||
photoViewer=new PhotoViewer(getActivity(), Collections.singletonList(fakeAttachment), 0, null, accountID, new PhotoViewer.Listener(){
|
photoViewer=new PhotoViewer(getActivity(), null, Collections.singletonList(fakeAttachment), 0, null, accountID, new PhotoViewer.Listener(){
|
||||||
@Override
|
@Override
|
||||||
public void setPhotoViewVisibility(int index, boolean visible){
|
public void setPhotoViewVisibility(int index, boolean visible){
|
||||||
image.setAlpha(visible ? 1f : 0f);
|
image.setAlpha(visible ? 1f : 0f);
|
||||||
|
|||||||
@@ -1134,7 +1134,7 @@ public class ProfileFragment extends LoaderFragment implements ScrollableToTop{
|
|||||||
if(ava==null)
|
if(ava==null)
|
||||||
return;
|
return;
|
||||||
int radius=V.dp(25);
|
int radius=V.dp(25);
|
||||||
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.avatar, ava), 0,
|
currentPhotoViewer=new PhotoViewer(getActivity(), null, createFakeAttachments(account.avatar, ava), 0,
|
||||||
null, accountID, 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1148,7 +1148,7 @@ public class ProfileFragment extends LoaderFragment implements ScrollableToTop{
|
|||||||
Drawable drawable=cover.getDrawable();
|
Drawable drawable=cover.getDrawable();
|
||||||
if(drawable==null || drawable instanceof ColorDrawable || account.headerStatic.endsWith("/missing.png"))
|
if(drawable==null || drawable instanceof ColorDrawable || account.headerStatic.endsWith("/missing.png"))
|
||||||
return;
|
return;
|
||||||
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.header, drawable), 0,
|
currentPhotoViewer=new PhotoViewer(getActivity(), null, createFakeAttachments(account.header, drawable), 0,
|
||||||
null, accountID, 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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package org.joinmastodon.android.ui.drawables;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
public class VideoPlayerSeekBarThumbDrawable extends Drawable{
|
||||||
|
private Paint thumbPaint=new Paint(Paint.ANTI_ALIAS_FLAG), clearPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
private Path clearPath=new Path();
|
||||||
|
|
||||||
|
public VideoPlayerSeekBarThumbDrawable(){
|
||||||
|
thumbPaint.setColor(0xffffffff);
|
||||||
|
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||||
|
clearPath.addRect(0, 0, V.dp(20), V.dp(32), Path.Direction.CW);
|
||||||
|
Path tmp=new Path();
|
||||||
|
float radius=V.dp(2);
|
||||||
|
tmp.addRoundRect(V.dp(-2), V.dp(12), V.dp(2), V.dp(20), radius, radius, Path.Direction.CW);
|
||||||
|
tmp.addRoundRect(V.dp(18), V.dp(12), V.dp(22), V.dp(20), radius, radius, Path.Direction.CW);
|
||||||
|
clearPath.op(tmp, Path.Op.DIFFERENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(@NonNull Canvas canvas){
|
||||||
|
Rect bounds=getBounds();
|
||||||
|
int thumbWidth=V.dp(4), thumbHeight=V.dp(32);
|
||||||
|
int thumbX=bounds.centerX()-thumbWidth/2, thumbY=bounds.centerY()-thumbHeight/2;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(thumbX-V.dp(8), thumbY);
|
||||||
|
canvas.drawPath(clearPath, clearPaint);
|
||||||
|
canvas.restore();
|
||||||
|
canvas.drawRoundRect(thumbX, thumbY, thumbX+thumbWidth, thumbY+thumbHeight, V.dp(2), V.dp(2), thumbPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(@Nullable ColorFilter colorFilter){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity(){
|
||||||
|
return PixelFormat.TRANSLUCENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicWidth(){
|
||||||
|
return V.dp(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicHeight(){
|
||||||
|
return V.dp(32);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,10 @@ import android.content.IntentFilter;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.Insets;
|
import android.graphics.Insets;
|
||||||
|
import android.graphics.Paint;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
@@ -28,17 +31,18 @@ import android.media.MediaPlayer;
|
|||||||
import android.media.MediaScannerConnection;
|
import android.media.MediaScannerConnection;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Property;
|
import android.util.Property;
|
||||||
import android.view.ContextThemeWrapper;
|
import android.view.ContextThemeWrapper;
|
||||||
import android.view.DisplayCutout;
|
import android.view.DisplayCutout;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.TextureView;
|
import android.view.TextureView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -53,18 +57,27 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.SeekBar;
|
import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.widget.Toolbar;
|
|
||||||
import android.window.OnBackInvokedDispatcher;
|
import android.window.OnBackInvokedDispatcher;
|
||||||
|
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.Snackbar;
|
import org.joinmastodon.android.ui.Snackbar;
|
||||||
|
import org.joinmastodon.android.ui.drawables.VideoPlayerSeekBarThumbDrawable;
|
||||||
|
import org.joinmastodon.android.ui.utils.BlurHashDecoder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -77,14 +90,17 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.palette.graphics.ColorUtils;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
import androidx.viewpager2.widget.ViewPager2;
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.imageloader.ImageCache;
|
import me.grishka.appkit.imageloader.ImageCache;
|
||||||
import me.grishka.appkit.imageloader.ViewImageLoader;
|
import me.grishka.appkit.imageloader.ViewImageLoader;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.BottomSheet;
|
||||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
||||||
import okio.BufferedSink;
|
import okio.BufferedSink;
|
||||||
import okio.Okio;
|
import okio.Okio;
|
||||||
@@ -97,11 +113,13 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
|
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
private List<Attachment> attachments;
|
private List<Attachment> attachments;
|
||||||
|
private int[] backgroundColors;
|
||||||
private int currentIndex;
|
private int currentIndex;
|
||||||
private WindowManager wm;
|
private WindowManager wm;
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
private Status status;
|
private Status status;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
private BaseStatusListFragment<?> parentFragment;
|
||||||
|
|
||||||
private FrameLayout windowView;
|
private FrameLayout windowView;
|
||||||
private FragmentRootLinearLayout uiOverlay;
|
private FragmentRootLinearLayout uiOverlay;
|
||||||
@@ -109,19 +127,24 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
private ColorDrawable background=new ColorDrawable(0xff000000);
|
private ColorDrawable background=new ColorDrawable(0xff000000);
|
||||||
private ArrayList<MediaPlayer> players=new ArrayList<>();
|
private ArrayList<MediaPlayer> players=new ArrayList<>();
|
||||||
private int screenOnRefCount=0;
|
private int screenOnRefCount=0;
|
||||||
private Toolbar toolbar;
|
|
||||||
private View toolbarWrap;
|
private View toolbarWrap;
|
||||||
private SeekBar videoSeekBar;
|
private SeekBar videoSeekBar;
|
||||||
private TextView videoTimeView;
|
private TextView videoTimeView;
|
||||||
private ImageButton videoPlayPauseButton;
|
private ImageButton videoPlayPauseButton;
|
||||||
private View videoControls;
|
private View videoControls;
|
||||||
|
private TextView altText;
|
||||||
|
private ImageButton backButton, downloadButton;
|
||||||
|
private View bottomBar;
|
||||||
|
private View postActions;
|
||||||
|
private View replyBtn, boostBtn, favoriteBtn, shareBtn, bookmarkBtn;
|
||||||
|
private TextView replyText, boostText, favoriteText;
|
||||||
private boolean uiVisible=true;
|
private boolean uiVisible=true;
|
||||||
private AudioManager.OnAudioFocusChangeListener audioFocusListener=this::onAudioFocusChanged;
|
private AudioManager.OnAudioFocusChangeListener audioFocusListener=this::onAudioFocusChanged;
|
||||||
private Runnable uiAutoHider=()->{
|
private Runnable uiAutoHider=()->{
|
||||||
if(uiVisible)
|
if(uiVisible)
|
||||||
toggleUI();
|
toggleUI();
|
||||||
};
|
};
|
||||||
private Animator currentSheetRelatedToolbarAnimation;
|
private Animator currentUiVisibilityAnimation;
|
||||||
|
|
||||||
private boolean videoPositionNeedsUpdating;
|
private boolean videoPositionNeedsUpdating;
|
||||||
private Runnable videoPositionUpdater=this::updateVideoPosition;
|
private Runnable videoPositionUpdater=this::updateVideoPosition;
|
||||||
@@ -157,13 +180,28 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public PhotoViewer(Activity activity, List<Attachment> attachments, int index, Status status, String accountID, Listener listener){
|
public PhotoViewer(Activity activity, BaseStatusListFragment<?> parentFragment, List<Attachment> attachments, int index, Status status, String accountID, Listener listener){
|
||||||
this.activity=activity;
|
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());
|
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;
|
currentIndex=index;
|
||||||
this.listener=listener;
|
this.listener=listener;
|
||||||
this.status=status;
|
this.status=status;
|
||||||
this.accountID=accountID;
|
this.accountID=accountID;
|
||||||
|
this.parentFragment=parentFragment;
|
||||||
|
|
||||||
|
backgroundColors=new int[this.attachments.size()];
|
||||||
|
int i=0;
|
||||||
|
float[] hsl=new float[3];
|
||||||
|
for(Attachment att:this.attachments){
|
||||||
|
if(TextUtils.isEmpty(att.blurhash)){
|
||||||
|
backgroundColors[i]=0xff000000;
|
||||||
|
}else{
|
||||||
|
ColorUtils.colorToHSL(BlurHashDecoder.decodeToSingleColor(att.blurhash) | 0xff000000, hsl);
|
||||||
|
hsl[2]=Math.min(hsl[2], 0.15f);
|
||||||
|
backgroundColors[i]=ColorUtils.HSLToColor(hsl);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
wm=activity.getWindowManager();
|
wm=activity.getWindowManager();
|
||||||
|
|
||||||
@@ -181,6 +219,9 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets){
|
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets){
|
||||||
|
int bottomInset=insets.getSystemWindowInsetBottom();
|
||||||
|
bottomBar.setPadding(bottomBar.getPaddingLeft(), bottomBar.getPaddingTop(), bottomBar.getPaddingRight(), bottomInset>0 ? Math.max(bottomInset+V.dp(8), V.dp(40)) : V.dp(12));
|
||||||
|
insets=insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0);
|
||||||
if(Build.VERSION.SDK_INT>=29){
|
if(Build.VERSION.SDK_INT>=29){
|
||||||
DisplayCutout cutout=insets.getDisplayCutout();
|
DisplayCutout cutout=insets.getDisplayCutout();
|
||||||
Insets tappable=insets.getTappableElementInsets();
|
Insets tappable=insets.getTappableElementInsets();
|
||||||
@@ -189,18 +230,14 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
int leftInset=Math.max(0, cutout.getSafeInsetLeft()-tappable.left);
|
int leftInset=Math.max(0, cutout.getSafeInsetLeft()-tappable.left);
|
||||||
int rightInset=Math.max(0, cutout.getSafeInsetRight()-tappable.right);
|
int rightInset=Math.max(0, cutout.getSafeInsetRight()-tappable.right);
|
||||||
toolbarWrap.setPadding(leftInset, 0, rightInset, 0);
|
toolbarWrap.setPadding(leftInset, 0, rightInset, 0);
|
||||||
videoControls.setPadding(leftInset, 0, rightInset, 0);
|
bottomBar.setPadding(leftInset, bottomBar.getPaddingTop(), rightInset, bottomBar.getPaddingBottom());
|
||||||
}else{
|
}else{
|
||||||
toolbarWrap.setPadding(0, 0, 0, 0);
|
toolbarWrap.setPadding(0, 0, 0, 0);
|
||||||
videoControls.setPadding(0, 0, 0, 0);
|
bottomBar.setPadding(0, bottomBar.getPaddingTop(), 0, bottomBar.getPaddingBottom());
|
||||||
}
|
}
|
||||||
insets=insets.replaceSystemWindowInsets(tappable.left, tappable.top, tappable.right, tappable.bottom);
|
insets=insets.replaceSystemWindowInsets(tappable.left, tappable.top, tappable.right, bottomBar.getVisibility()==View.VISIBLE ? 0 : tappable.bottom);
|
||||||
}
|
}
|
||||||
uiOverlay.dispatchApplyWindowInsets(insets);
|
uiOverlay.dispatchApplyWindowInsets(insets);
|
||||||
int bottomInset=insets.getSystemWindowInsetBottom();
|
|
||||||
if(bottomInset>0 && bottomInset<V.dp(36)){
|
|
||||||
uiOverlay.setPadding(uiOverlay.getPaddingLeft(), uiOverlay.getPaddingTop(), uiOverlay.getPaddingRight(), V.dp(36));
|
|
||||||
}
|
|
||||||
return insets.consumeSystemWindowInsets();
|
return insets.consumeSystemWindowInsets();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -214,6 +251,11 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
public void onPageSelected(int position){
|
public void onPageSelected(int position){
|
||||||
onPageChanged(position);
|
onPageChanged(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
|
||||||
|
updateBackgroundColor(position, positionOffset);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
windowView.addView(pager);
|
windowView.addView(pager);
|
||||||
pager.setMotionEventSplittingEnabled(false);
|
pager.setMotionEventSplittingEnabled(false);
|
||||||
@@ -222,19 +264,22 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
uiOverlay.setStatusBarColor(0x80000000);
|
uiOverlay.setStatusBarColor(0x80000000);
|
||||||
uiOverlay.setNavigationBarColor(0x80000000);
|
uiOverlay.setNavigationBarColor(0x80000000);
|
||||||
toolbarWrap=uiOverlay.findViewById(R.id.toolbar_wrap);
|
toolbarWrap=uiOverlay.findViewById(R.id.toolbar_wrap);
|
||||||
toolbar=uiOverlay.findViewById(R.id.toolbar);
|
backButton=uiOverlay.findViewById(R.id.btn_back);
|
||||||
toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0));
|
backButton.setOnClickListener(v->onStartSwipeToDismissTransition(0));
|
||||||
if(status!=null)
|
downloadButton=uiOverlay.findViewById(R.id.btn_download);
|
||||||
toolbar.getMenu().add(R.string.info).setIcon(R.drawable.ic_info_24px).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
downloadButton.setOnClickListener(v->saveCurrentFile());
|
||||||
else
|
bottomBar=uiOverlay.findViewById(R.id.bottom_bar);
|
||||||
toolbar.getMenu().add(R.string.download).setIcon(R.drawable.ic_download_24px).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
postActions=uiOverlay.findViewById(R.id.post_actions);
|
||||||
toolbar.setOnMenuItemClickListener(item->{
|
|
||||||
if(status!=null)
|
replyBtn=uiOverlay.findViewById(R.id.reply_btn);
|
||||||
showInfoSheet();
|
boostBtn=uiOverlay.findViewById(R.id.boost_btn);
|
||||||
else
|
favoriteBtn=uiOverlay.findViewById(R.id.favorite_btn);
|
||||||
saveCurrentFile();
|
bookmarkBtn=uiOverlay.findViewById(R.id.bookmark_btn);
|
||||||
return true;
|
shareBtn=uiOverlay.findViewById(R.id.share_btn);
|
||||||
});
|
replyText=uiOverlay.findViewById(R.id.reply);
|
||||||
|
boostText=uiOverlay.findViewById(R.id.boost);
|
||||||
|
favoriteText=uiOverlay.findViewById(R.id.favorite);
|
||||||
|
|
||||||
uiOverlay.setAlpha(0f);
|
uiOverlay.setAlpha(0f);
|
||||||
videoControls=uiOverlay.findViewById(R.id.video_player_controls);
|
videoControls=uiOverlay.findViewById(R.id.video_player_controls);
|
||||||
videoSeekBar=uiOverlay.findViewById(R.id.seekbar);
|
videoSeekBar=uiOverlay.findViewById(R.id.seekbar);
|
||||||
@@ -247,6 +292,25 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
videoLastTimeUpdatePosition=-1;
|
videoLastTimeUpdatePosition=-1;
|
||||||
updateVideoTimeText(0);
|
updateVideoTimeText(0);
|
||||||
}
|
}
|
||||||
|
altText=uiOverlay.findViewById(R.id.alt_text);
|
||||||
|
altText.setOnClickListener(v->showAltTextSheet());
|
||||||
|
updateAltText();
|
||||||
|
updateBackgroundColor(currentIndex, 0);
|
||||||
|
|
||||||
|
if(status==null){
|
||||||
|
bottomBar.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
Paint paint=new Paint();
|
||||||
|
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
|
||||||
|
postActions.setLayerType(View.LAYER_TYPE_HARDWARE, paint);
|
||||||
|
updatePostActions();
|
||||||
|
|
||||||
|
replyBtn.setOnClickListener(this::onPostActionClick);
|
||||||
|
boostBtn.setOnClickListener(this::onPostActionClick);
|
||||||
|
favoriteBtn.setOnClickListener(this::onPostActionClick);
|
||||||
|
bookmarkBtn.setOnClickListener(this::onPostActionClick);
|
||||||
|
shareBtn.setOnClickListener(this::onPostActionClick);
|
||||||
|
}
|
||||||
|
|
||||||
WindowManager.LayoutParams wlp=new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
|
WindowManager.LayoutParams wlp=new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
|
||||||
wlp.type=WindowManager.LayoutParams.TYPE_APPLICATION;
|
wlp.type=WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||||
@@ -296,6 +360,11 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
if(fromUser){
|
if(fromUser){
|
||||||
float p=progress/10000f;
|
float p=progress/10000f;
|
||||||
updateVideoTimeText(Math.round(p*videoDuration));
|
updateVideoTimeText(Math.round(p*videoDuration));
|
||||||
|
|
||||||
|
// This moves the time view in sync with the seekbar thumb, but also makes sure it doesn't go off screen
|
||||||
|
// (there must be at least 16dp between the time and the edge of the screen)
|
||||||
|
float timeX=p*(seekBar.getWidth()-V.dp(32))+V.dp(16)-videoTimeView.getWidth()/2f;
|
||||||
|
videoTimeView.setTranslationX(Math.max(-(videoTimeView.getLeft()-V.dp(16)), Math.min(timeX, videoControls.getWidth()-V.dp(16)-videoTimeView.getWidth()-videoTimeView.getLeft())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +374,14 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
if(!uiVisible) // If dragging started during hide animation
|
if(!uiVisible) // If dragging started during hide animation
|
||||||
toggleUI();
|
toggleUI();
|
||||||
windowView.removeCallbacks(uiAutoHider);
|
windowView.removeCallbacks(uiAutoHider);
|
||||||
|
V.setVisibilityAnimated(videoTimeView, View.VISIBLE);
|
||||||
|
postActions.animate().alpha(0f).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
|
altText.animate().alpha(0f).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
|
if(altText.getVisibility()==View.VISIBLE){
|
||||||
|
videoTimeView.setTranslationY(seekBar.getHeight()+V.dp(12));
|
||||||
|
}else{
|
||||||
|
videoTimeView.setTranslationY(-videoTimeView.getHeight()-V.dp(12));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -312,15 +389,24 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
MediaPlayer player=findCurrentVideoPlayer();
|
MediaPlayer player=findCurrentVideoPlayer();
|
||||||
if(player!=null){
|
if(player!=null){
|
||||||
float progress=seekBar.getProgress()/10000f;
|
float progress=seekBar.getProgress()/10000f;
|
||||||
player.seekTo(Math.round(progress*player.getDuration()));
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O)
|
||||||
|
player.seekTo(Math.round(progress*player.getDuration()), MediaPlayer.SEEK_CLOSEST);
|
||||||
|
else
|
||||||
|
player.seekTo(Math.round(progress*player.getDuration()));
|
||||||
}
|
}
|
||||||
hideUiDelayed();
|
hideUiDelayed();
|
||||||
|
V.setVisibilityAnimated(videoTimeView, View.INVISIBLE);
|
||||||
|
postActions.animate().alpha(1f).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
|
altText.animate().alpha(1f).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
videoSeekBar.setThumb(new VideoPlayerSeekBarThumbDrawable());
|
||||||
|
|
||||||
|
E.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeMenu(){
|
public void removeMenu(){
|
||||||
toolbar.getMenu().clear();
|
downloadButton.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -371,7 +457,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
.alpha(0)
|
.alpha(0)
|
||||||
.setDuration(300)
|
.setDuration(300)
|
||||||
.setInterpolator(CubicBezierInterpolator.DEFAULT)
|
.setInterpolator(CubicBezierInterpolator.DEFAULT)
|
||||||
.withEndAction(()->wm.removeView(windowView))
|
.withEndAction(this::onDismissed)
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,6 +485,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
if(receiverRegistered){
|
if(receiverRegistered){
|
||||||
activity.unregisterReceiver(downloadCompletedReceiver);
|
activity.unregisterReceiver(downloadCompletedReceiver);
|
||||||
}
|
}
|
||||||
|
E.unregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -407,21 +494,45 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void toggleUI(){
|
private void toggleUI(){
|
||||||
|
if(currentUiVisibilityAnimation!=null)
|
||||||
|
currentUiVisibilityAnimation.cancel();
|
||||||
if(uiVisible){
|
if(uiVisible){
|
||||||
uiOverlay.animate()
|
AnimatorSet set=new AnimatorSet();
|
||||||
.alpha(0f)
|
set.playTogether(
|
||||||
.setDuration(250)
|
ObjectAnimator.ofFloat(uiOverlay, View.ALPHA, 0f),
|
||||||
.setInterpolator(CubicBezierInterpolator.DEFAULT)
|
ObjectAnimator.ofFloat(toolbarWrap, View.TRANSLATION_Y, V.dp(-32)),
|
||||||
.withEndAction(()->uiOverlay.setVisibility(View.GONE))
|
ObjectAnimator.ofFloat(bottomBar, View.TRANSLATION_Y, V.dp(32))
|
||||||
.start();
|
);
|
||||||
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
set.setDuration(250);
|
||||||
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation){
|
||||||
|
uiOverlay.setVisibility(View.GONE);
|
||||||
|
currentUiVisibilityAnimation=null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
currentUiVisibilityAnimation=set;
|
||||||
|
set.start();
|
||||||
windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN);
|
windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN);
|
||||||
}else{
|
}else{
|
||||||
uiOverlay.setVisibility(View.VISIBLE);
|
uiOverlay.setVisibility(View.VISIBLE);
|
||||||
uiOverlay.animate()
|
AnimatorSet set=new AnimatorSet();
|
||||||
.alpha(1f)
|
set.playTogether(
|
||||||
.setDuration(300)
|
ObjectAnimator.ofFloat(uiOverlay, View.ALPHA, 1f),
|
||||||
.setInterpolator(CubicBezierInterpolator.DEFAULT)
|
ObjectAnimator.ofFloat(toolbarWrap, View.TRANSLATION_Y, 0),
|
||||||
.start();
|
ObjectAnimator.ofFloat(bottomBar, View.TRANSLATION_Y, 0)
|
||||||
|
);
|
||||||
|
set.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
set.setDuration(300);
|
||||||
|
set.addListener(new AnimatorListenerAdapter(){
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation){
|
||||||
|
currentUiVisibilityAnimation=null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
currentUiVisibilityAnimation=set;
|
||||||
|
set.start();
|
||||||
windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() & ~(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN));
|
windowView.setSystemUiVisibility(windowView.getSystemUiVisibility() & ~(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN));
|
||||||
if(attachments.get(currentIndex).type==Attachment.Type.VIDEO)
|
if(attachments.get(currentIndex).type==Attachment.Type.VIDEO)
|
||||||
hideUiDelayed(5000);
|
hideUiDelayed(5000);
|
||||||
@@ -448,6 +559,105 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
videoLastTimeUpdatePosition=-1;
|
videoLastTimeUpdatePosition=-1;
|
||||||
updateVideoTimeText(0);
|
updateVideoTimeText(0);
|
||||||
}
|
}
|
||||||
|
updateAltText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAltText(){
|
||||||
|
Attachment att=attachments.get(currentIndex);
|
||||||
|
if(TextUtils.isEmpty(att.description)){
|
||||||
|
altText.setVisibility(View.GONE);
|
||||||
|
}else{
|
||||||
|
altText.setVisibility(View.VISIBLE);
|
||||||
|
altText.setText(att.description);
|
||||||
|
altText.setMaxLines(att.type==Attachment.Type.VIDEO ? 3 : 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBackgroundColor(int position, float positionOffset){
|
||||||
|
int color;
|
||||||
|
if(positionOffset==0){
|
||||||
|
color=backgroundColors[position];
|
||||||
|
}else{
|
||||||
|
color=UiUtils.alphaBlendColors(backgroundColors[position], backgroundColors[position+1], positionOffset);
|
||||||
|
}
|
||||||
|
int alpha=background.getAlpha();
|
||||||
|
background.setColor(color);
|
||||||
|
background.setAlpha(alpha);
|
||||||
|
uiOverlay.setStatusBarColor(color & 0xe6ffffff);
|
||||||
|
uiOverlay.setNavigationBarColor(color & 0xe6ffffff);
|
||||||
|
bottomBar.setBackgroundTintList(ColorStateList.valueOf(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePostActions(){
|
||||||
|
bindActionButton(replyText, status.repliesCount);
|
||||||
|
bindActionButton(boostText, status.reblogsCount);
|
||||||
|
bindActionButton(favoriteText, status.favouritesCount);
|
||||||
|
boostBtn.setSelected(status.reblogged);
|
||||||
|
favoriteBtn.setSelected(status.favourited);
|
||||||
|
bookmarkBtn.setSelected(status.bookmarked);
|
||||||
|
bookmarkBtn.setContentDescription(activity.getString(status.bookmarked ? R.string.remove_bookmark : R.string.add_bookmark));
|
||||||
|
boolean isOwn=status.account.id.equals(AccountSessionManager.getInstance().getAccount(accountID).self.id);
|
||||||
|
boostBtn.setEnabled(status.visibility==StatusPrivacy.PUBLIC || status.visibility==StatusPrivacy.UNLISTED
|
||||||
|
|| (status.visibility==StatusPrivacy.PRIVATE && isOwn));
|
||||||
|
boostBtn.setAlpha(boostBtn.isEnabled() ? 1 : 0.5f);
|
||||||
|
Drawable d=activity.getResources().getDrawable(switch(status.visibility){
|
||||||
|
case PUBLIC, UNLISTED -> R.drawable.ic_boost;
|
||||||
|
case PRIVATE -> isOwn ? R.drawable.ic_boost_private : R.drawable.ic_boost_disabled_24px;
|
||||||
|
case DIRECT -> R.drawable.ic_boost_disabled_24px;
|
||||||
|
}, activity.getTheme());
|
||||||
|
d.setBounds(0, 0, V.dp(20), V.dp(20));
|
||||||
|
boostText.setCompoundDrawablesRelative(d, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bindActionButton(TextView btn, long count){
|
||||||
|
if(count>0){
|
||||||
|
btn.setText(UiUtils.abbreviateNumber(count));
|
||||||
|
btn.setCompoundDrawablePadding(V.dp(6));
|
||||||
|
}else{
|
||||||
|
btn.setText("");
|
||||||
|
btn.setCompoundDrawablePadding(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPostActionClick(View view){
|
||||||
|
int id=view.getId();
|
||||||
|
if(id==R.id.boost_btn){
|
||||||
|
if(status!=null){
|
||||||
|
AccountSessionManager.get(accountID).getStatusInteractionController().setReblogged(status, !status.reblogged);
|
||||||
|
}
|
||||||
|
}else if(id==R.id.favorite_btn){
|
||||||
|
if(status!=null){
|
||||||
|
AccountSessionManager.get(accountID).getStatusInteractionController().setFavorited(status, !status.favourited);
|
||||||
|
}
|
||||||
|
}else if(id==R.id.share_btn){
|
||||||
|
if(status!=null){
|
||||||
|
UiUtils.openSystemShareSheet(activity, status);
|
||||||
|
}
|
||||||
|
}else if(id==R.id.bookmark_btn){
|
||||||
|
if(status!=null){
|
||||||
|
AccountSessionManager.get(accountID).getStatusInteractionController().setBookmarked(status, !status.bookmarked);
|
||||||
|
}
|
||||||
|
}else if(id==R.id.reply_btn){
|
||||||
|
parentFragment.maybeShowPreReplySheet(status, ()->{
|
||||||
|
onDismissed();
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
args.putParcelable("replyTo", Parcels.wrap(status));
|
||||||
|
Nav.go(activity, ComposeFragment.class, args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
|
||||||
|
if(status!=null && ev.id.equals(status.id)){
|
||||||
|
status.reblogsCount=ev.reblogs;
|
||||||
|
status.favouritesCount=ev.favorites;
|
||||||
|
status.reblogged=ev.reblogged;
|
||||||
|
status.favourited=ev.favorited;
|
||||||
|
status.bookmarked=ev.bookmarked;
|
||||||
|
updatePostActions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -690,91 +900,12 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showInfoSheet(){
|
private void showAltTextSheet(){
|
||||||
pauseVideo();
|
pauseVideo();
|
||||||
PhotoViewerInfoSheet sheet=new PhotoViewerInfoSheet(new ContextThemeWrapper(activity, R.style.Theme_Mastodon_Dark), attachments.get(currentIndex), toolbar.getHeight(), new PhotoViewerInfoSheet.Listener(){
|
BottomSheet sheet=new AltTextSheet(new ContextThemeWrapper(activity, UiUtils.getThemeForUserPreference(activity, GlobalUserPreferences.ThemePreference.DARK)),
|
||||||
private boolean ignoreBeforeDismiss;
|
attachments.get(currentIndex));
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}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();
|
sheet.show();
|
||||||
if(currentSheetRelatedToolbarAnimation!=null)
|
sheet.getWindow().getDecorView().setSystemUiVisibility(sheet.getWindow().getDecorView().getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
|
||||||
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{
|
public interface Listener{
|
||||||
|
|||||||
@@ -1,182 +0,0 @@
|
|||||||
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(me.grishka.appkit.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.setContentDescription(context.getString(R.string.back));
|
|
||||||
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.setContentDescription(context.getString(R.string.info));
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -58,6 +58,12 @@ public class BlurHashDecoder{
|
|||||||
return composeBitmap(width, height, numCompX, numCompY, colors, useCache);
|
return composeBitmap(width, height, numCompX, numCompY, colors, useCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int decodeToSingleColor(String hash){
|
||||||
|
if(hash.length()<6)
|
||||||
|
return 0;
|
||||||
|
return decode83(hash, 2, 6) & 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
private static int decode83(String str, int from, int to){
|
private static int decode83(String str, int from, int to){
|
||||||
int result=0;
|
int result=0;
|
||||||
for(int i=from;i<to;i++){
|
for(int i=from;i<to;i++){
|
||||||
|
|||||||
@@ -725,7 +725,11 @@ public class UiUtils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void setUserPreferredTheme(Context context){
|
public static void setUserPreferredTheme(Context context){
|
||||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
context.setTheme(getThemeForUserPreference(context, GlobalUserPreferences.theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getThemeForUserPreference(Context context, GlobalUserPreferences.ThemePreference pref){
|
||||||
|
return switch(pref){
|
||||||
case AUTO -> switch(getColorContrastMode(context)){
|
case AUTO -> switch(getColorContrastMode(context)){
|
||||||
case DEFAULT -> R.style.Theme_Mastodon_AutoLightDark;
|
case DEFAULT -> R.style.Theme_Mastodon_AutoLightDark;
|
||||||
case MEDIUM -> R.style.Theme_Mastodon_AutoLightDark_MediumContrast;
|
case MEDIUM -> R.style.Theme_Mastodon_AutoLightDark_MediumContrast;
|
||||||
@@ -741,7 +745,7 @@ public class UiUtils{
|
|||||||
case MEDIUM -> R.style.Theme_Mastodon_Dark_MediumContrast;
|
case MEDIUM -> R.style.Theme_Mastodon_Dark_MediumContrast;
|
||||||
case HIGH -> R.style.Theme_Mastodon_Dark_HighContrast;
|
case HIGH -> R.style.Theme_Mastodon_Dark_HighContrast;
|
||||||
};
|
};
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDarkTheme(){
|
public static boolean isDarkTheme(){
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package org.joinmastodon.android.ui.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
|
import android.graphics.Shader;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
|
||||||
|
import me.grishka.appkit.utils.CustomViewHelper;
|
||||||
|
|
||||||
|
public class PhotoViewerAltTextView extends TextView implements CustomViewHelper{
|
||||||
|
private String moreText;
|
||||||
|
private Paint morePaint=new Paint(), clearPaint=new Paint();
|
||||||
|
private Matrix matrix=new Matrix();
|
||||||
|
private LinearGradient gradient, rtlGradient;
|
||||||
|
|
||||||
|
public PhotoViewerAltTextView(Context context){
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhotoViewerAltTextView(Context context, AttributeSet attrs){
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PhotoViewerAltTextView(Context context, AttributeSet attrs, int defStyle){
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
moreText=context.getString(R.string.text_show_more).toUpperCase();
|
||||||
|
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
|
||||||
|
gradient=new LinearGradient(0, 0, dp(56), 0, 0x00ffffff, 0xffffffff, Shader.TileMode.CLAMP);
|
||||||
|
rtlGradient=new LinearGradient(0, 0, dp(56), 0, 0xffffffff, 0x00ffffff, Shader.TileMode.CLAMP);
|
||||||
|
setLayerType(LAYER_TYPE_HARDWARE, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas){
|
||||||
|
super.onDraw(canvas);
|
||||||
|
Layout layout=getLayout();
|
||||||
|
if(layout.getLineCount()>=getMaxLines() && layout.getEllipsisCount(layout.getLineCount()-1)>0){
|
||||||
|
int lastLine=layout.getLineCount()-1;
|
||||||
|
morePaint.set(getPaint());
|
||||||
|
morePaint.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
float moreWidth=morePaint.measureText(moreText);
|
||||||
|
int lineTop=layout.getLineTop(lastLine);
|
||||||
|
int lineBottom=layout.getLineBottom(lastLine);
|
||||||
|
int viewRight=getWidth()-getPaddingRight();
|
||||||
|
int gradientWidth=dp(56);
|
||||||
|
|
||||||
|
if(layout.getParagraphDirection(lastLine)==Layout.DIR_RIGHT_TO_LEFT){
|
||||||
|
matrix.setTranslate(getPaddingLeft()+moreWidth, lineTop);
|
||||||
|
rtlGradient.setLocalMatrix(matrix);
|
||||||
|
clearPaint.setShader(rtlGradient);
|
||||||
|
canvas.drawRect(getPaddingLeft(), lineTop, getPaddingLeft()+moreWidth+gradientWidth, lineBottom, clearPaint);
|
||||||
|
canvas.drawText(moreText, getPaddingLeft(), layout.getLineBaseline(lastLine), morePaint);
|
||||||
|
}else{
|
||||||
|
matrix.setTranslate(viewRight-moreWidth-gradientWidth, lineTop);
|
||||||
|
gradient.setLocalMatrix(matrix);
|
||||||
|
clearPaint.setShader(gradient);
|
||||||
|
canvas.drawRect(viewRight-moreWidth-gradientWidth, lineTop, viewRight, lineBottom, clearPaint);
|
||||||
|
canvas.drawText(moreText, viewRight-moreWidth, layout.getLineBaseline(lastLine), morePaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:type="linear"
|
||||||
|
android:startColor="#00000000"
|
||||||
|
android:endColor="#E6000000"
|
||||||
|
android:angle="270"/>
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/m3_white_overlay">
|
||||||
|
<item android:gravity="center" android:width="32dp" android:height="32dp">
|
||||||
|
<shape android:shape="oval">
|
||||||
|
<solid android:color="#80000000"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
9
mastodon/src/main/res/drawable/ic_bookmark_20px.xml
Normal file
9
mastodon/src/main/res/drawable/ic_bookmark_20px.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M240,816L240,216Q240,186.3 261.15,165.15Q282.3,144 312,144L648,144Q677.7,144 698.85,165.15Q720,186.3 720,216L720,816L480,720L240,816ZM312,709L480,642L648,709L648,216Q648,216 648,216Q648,216 648,216L312,216Q312,216 312,216Q312,216 312,216L312,709ZM312,216L312,216Q312,216 312,216Q312,216 312,216L648,216Q648,216 648,216Q648,216 648,216L648,216L480,216L312,216Z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_bookmark_fill1_20px" android:state_selected="true"/>
|
||||||
|
<item android:drawable="@drawable/ic_bookmark_20px"/>
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M240,816L240,216Q240,186 261,165Q282,144 312,144L648,144Q678,144 699,165Q720,186 720,216L720,816L480,720L240,816Z"/>
|
||||||
|
</vector>
|
||||||
9
mastodon/src/main/res/drawable/ic_close_20px.xml
Normal file
9
mastodon/src/main/res/drawable/ic_close_20px.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M291,720L240,669L429,480L240,291L291,240L480,429L669,240L720,291L531,480L720,669L669,720L480,531L291,720Z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,27 +1,23 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:gravity="center_vertical">
|
<item android:gravity="center_vertical" android:left="-4dp" android:right="-4dp">
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="#69ffffff"/>
|
<solid android:color="#40ffffff"/>
|
||||||
<corners android:radius="1dp"/>
|
<corners android:radius="16dp"/>
|
||||||
<size android:height="2dp"/>
|
<size android:height="8dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
<item android:gravity="center_vertical" android:id="@android:id/secondaryProgress">
|
<item android:gravity="center_vertical|end" android:width="4dp" android:height="4dp" android:end="-2dp">
|
||||||
<clip>
|
<shape android:shape="oval">
|
||||||
<shape>
|
<solid android:color="#fff"/>
|
||||||
<solid android:color="#40ffffff"/>
|
</shape>
|
||||||
<corners android:radius="1dp"/>
|
|
||||||
<size android:height="2dp"/>
|
|
||||||
</shape>
|
|
||||||
</clip>
|
|
||||||
</item>
|
</item>
|
||||||
<item android:gravity="center_vertical" android:id="@android:id/progress">
|
<item android:gravity="center_vertical" android:id="@android:id/progress" android:left="-4dp" android:right="-4dp">
|
||||||
<clip>
|
<clip>
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="#fff"/>
|
<solid android:color="#fff"/>
|
||||||
<corners android:radius="1dp"/>
|
<corners android:radius="16dp"/>
|
||||||
<size android:height="2dp"/>
|
<size android:height="8dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
</clip>
|
</clip>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<me.grishka.appkit.views.FragmentRootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<me.grishka.appkit.views.FragmentRootLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:id="@+id/photo_viewer_overlay"
|
android:id="@+id/photo_viewer_overlay"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -13,66 +14,222 @@
|
|||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/toolbar_wrap"
|
android:id="@+id/toolbar_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="?android:actionBarSize"
|
||||||
android:layout_gravity="top"
|
android:layout_gravity="top">
|
||||||
android:background="#80000000">
|
|
||||||
|
|
||||||
<Toolbar
|
<ImageButton
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/btn_back"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="48dp"
|
||||||
android:layout_height="?android:attr/actionBarSize"
|
android:layout_height="48dp"
|
||||||
android:elevation="0dp"
|
android:layout_gravity="center_vertical|start"
|
||||||
android:navigationIcon="@drawable/ic_arrow_back"
|
android:layout_marginStart="8dp"
|
||||||
android:navigationContentDescription="@string/back"
|
android:src="@drawable/ic_close_20px"
|
||||||
android:theme="@style/Theme.Mastodon.Toolbar.Profile"
|
android:tint="#fff"
|
||||||
android:background="@null"/>
|
android:background="@drawable/bg_photo_viewer_toolbar_button"
|
||||||
|
android:contentDescription="@string/back"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_download"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:src="@drawable/ic_download_20px"
|
||||||
|
android:tint="#fff"
|
||||||
|
android:background="@drawable/bg_photo_viewer_toolbar_button"
|
||||||
|
android:contentDescription="@string/download"/>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
android:id="@+id/video_player_controls"
|
android:id="@+id/bottom_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:background="#80000000">
|
android:orientation="vertical"
|
||||||
|
android:paddingTop="64dp"
|
||||||
|
android:background="@drawable/bg_photo_viewer_bottom"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToPadding="false">
|
||||||
|
|
||||||
<SeekBar
|
<RelativeLayout
|
||||||
android:id="@+id/seekbar"
|
android:id="@+id/video_player_controls"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="34dp"
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/play_pause_btn"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:src="@drawable/ic_pause_24"
|
||||||
|
android:tint="#fff"
|
||||||
|
android:contentDescription="@string/pause"
|
||||||
|
android:background="?android:selectableItemBackgroundBorderless"/>
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/seekbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_toEndOf="@id/play_pause_btn"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:max="10000"
|
||||||
|
android:splitTrack="false"
|
||||||
|
android:layerType="hardware"
|
||||||
|
android:background="@null"
|
||||||
|
android:progressDrawable="@drawable/seekbar_video_player"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignStart="@id/seekbar"
|
||||||
|
android:layout_alignTop="@id/seekbar"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
android:textColor="#e6ffffff"
|
||||||
|
android:fontFeatureSettings="'tnum'"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:visibility="invisible"
|
||||||
|
tools:text="1:23 / 4:56"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.PhotoViewerAltTextView
|
||||||
|
android:id="@+id/alt_text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:textAppearance="@style/m3_body_medium"
|
||||||
|
android:textColor="#E6FFFFFF"
|
||||||
|
android:ellipsize="end"
|
||||||
|
tools:text="Alt text goes here"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/post_actions"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginBottom="-8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:max="10000"
|
android:paddingLeft="8dp"
|
||||||
android:progressDrawable="@drawable/seekbar_video_player"
|
android:paddingRight="8dp">
|
||||||
android:thumb="@drawable/seekbar_video_player_thumb"/>
|
|
||||||
|
|
||||||
<ImageButton
|
<FrameLayout
|
||||||
android:id="@+id/play_pause_btn"
|
android:id="@+id/reply_btn"
|
||||||
android:layout_width="40dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="40dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@id/seekbar"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_alignParentStart="true"
|
android:paddingHorizontal="8dp"
|
||||||
android:layout_marginLeft="8dp"
|
android:background="?android:actionBarItemBackground"
|
||||||
android:layout_marginRight="8dp"
|
android:minWidth="64dp">
|
||||||
android:layout_marginBottom="8dp"
|
<TextView
|
||||||
android:src="@drawable/ic_pause_24"
|
android:id="@+id/reply"
|
||||||
android:tint="#fff"
|
android:layout_width="wrap_content"
|
||||||
android:contentDescription="@string/pause"
|
android:layout_height="24dp"
|
||||||
android:background="?android:selectableItemBackgroundBorderless"/>
|
android:layout_gravity="center|start"
|
||||||
|
android:drawableStart="@drawable/ic_reply_20px"
|
||||||
|
android:drawablePadding="6dp"
|
||||||
|
android:drawableTint="#80ffffff"
|
||||||
|
android:textColor="#80ffffff"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/m3_label_medium"
|
||||||
|
android:duplicateParentState="true"
|
||||||
|
tools:text="123"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<TextView
|
<FrameLayout
|
||||||
android:id="@+id/time"
|
android:id="@+id/boost_btn"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@id/seekbar"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_alignParentEnd="true"
|
android:paddingHorizontal="8dp"
|
||||||
android:layout_marginTop="16dp"
|
android:background="?android:actionBarItemBackground"
|
||||||
android:layout_marginEnd="16dp"
|
android:minWidth="64dp">
|
||||||
android:gravity="center_vertical"
|
<TextView
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:id="@+id/boost"
|
||||||
android:textColor="#fff"
|
android:layout_width="wrap_content"
|
||||||
tools:text="1:23 / 4:56"/>
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center|start"
|
||||||
|
android:drawableStart="@drawable/ic_repeat_selector"
|
||||||
|
android:drawablePadding="6dp"
|
||||||
|
android:drawableTint="#80ffffff"
|
||||||
|
android:textColor="#80ffffff"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/m3_label_medium"
|
||||||
|
android:duplicateParentState="true"
|
||||||
|
tools:text="123"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
<FrameLayout
|
||||||
|
android:id="@+id/favorite_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:background="?android:actionBarItemBackground"
|
||||||
|
android:minWidth="64dp">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/favorite"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center|start"
|
||||||
|
android:drawableStart="@drawable/ic_star_selector"
|
||||||
|
android:drawablePadding="6dp"
|
||||||
|
android:drawableTint="#80ffffff"
|
||||||
|
android:textColor="#80ffffff"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/m3_label_medium"
|
||||||
|
android:duplicateParentState="true"
|
||||||
|
tools:text="123"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
android:layout_width="0px"
|
||||||
|
android:layout_height="1px"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/bookmark_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:background="?android:actionBarItemBackground"
|
||||||
|
android:minWidth="34dp">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/bookmark"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_bookmark_20px_selector"
|
||||||
|
android:tint="#80ffffff"
|
||||||
|
android:tintMode="src_in"
|
||||||
|
android:duplicateParentState="true"
|
||||||
|
android:gravity="center_vertical"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/share_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingHorizontal="8dp"
|
||||||
|
android:background="?android:actionBarItemBackground"
|
||||||
|
android:minWidth="34dp">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/share"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@drawable/ic_share_20px"
|
||||||
|
android:tint="#80ffffff"
|
||||||
|
android:tintMode="src_in"
|
||||||
|
android:contentDescription="@string/share_toot_title"
|
||||||
|
android:gravity="center_vertical"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -816,4 +816,5 @@
|
|||||||
<string name="moderation_warning_action_silence">Your account has been limited.</string>
|
<string name="moderation_warning_action_silence">Your account has been limited.</string>
|
||||||
<string name="moderation_warning_action_suspend">Your account has been suspended.</string>
|
<string name="moderation_warning_action_suspend">Your account has been suspended.</string>
|
||||||
<string name="moderation_warning_learn_more">Learn more</string>
|
<string name="moderation_warning_learn_more">Learn more</string>
|
||||||
|
<string name="text_show_more">More</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user