Gifv player

This commit is contained in:
Grishka 2022-02-08 23:37:48 +03:00
parent 990f364bf9
commit 90bd7baa94
10 changed files with 362 additions and 81 deletions

View File

@ -15,6 +15,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ImageStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PhotoStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
@ -122,18 +123,18 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
public void openPhotoViewer(String parentID, Status _status, int attachmentIndex){
final Status status=_status.reblog!=null ? _status.reblog : _status;
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
private PhotoStatusDisplayItem.Holder transitioningHolder;
private ImageStatusDisplayItem.Holder transitioningHolder;
@Override
public void setPhotoViewVisibility(int index, boolean visible){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
ImageStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null)
holder.photo.setAlpha(visible ? 1f : 0f);
}
@Override
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
ImageStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null){
transitioningHolder=holder;
View view=transitioningHolder.photo;
@ -170,7 +171,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
@Override
public Drawable getPhotoViewCurrentDrawable(int index){
PhotoStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
ImageStatusDisplayItem.Holder holder=findPhotoViewHolder(index);
if(holder!=null)
return holder.photo.getDrawable();
return null;
@ -181,14 +182,14 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
currentPhotoViewer=null;
}
private PhotoStatusDisplayItem.Holder findPhotoViewHolder(int index){
private ImageStatusDisplayItem.Holder findPhotoViewHolder(int index){
int offset=0;
for(StatusDisplayItem item:displayItems){
if(item.parentID.equals(parentID)){
if(item instanceof PhotoStatusDisplayItem){
if(item instanceof ImageStatusDisplayItem){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(getMainAdapterOffset()+offset+index);
if(holder instanceof PhotoStatusDisplayItem.Holder){
return (PhotoStatusDisplayItem.Holder) holder;
if(holder instanceof ImageStatusDisplayItem.Holder){
return (ImageStatusDisplayItem.Holder) holder;
}
return null;
}
@ -264,10 +265,10 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
position-=getMainAdapterOffset();
if(position>=0 && position<displayItems.size()){
StatusDisplayItem item=displayItems.get(position);
if(item instanceof PhotoStatusDisplayItem){
int total=((PhotoStatusDisplayItem) item).totalPhotos;
if(item instanceof ImageStatusDisplayItem){
int total=((ImageStatusDisplayItem) item).totalPhotos;
if(total>1){
int index=((PhotoStatusDisplayItem) item).index;
int index=((ImageStatusDisplayItem) item).index;
return 1;
}
}

View File

@ -0,0 +1,41 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.graphics.Outline;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
public class GifVStatusDisplayItem extends ImageStatusDisplayItem{
public GifVStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos){
super(parentID, parentFragment, photo, status, index, totalPhotos);
request=new UrlImageLoaderRequest(photo.previewUrl, 1000, 1000);
}
@Override
public Type getType(){
return Type.GIFV;
}
public static class Holder extends ImageStatusDisplayItem.Holder<GifVStatusDisplayItem>{
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_gifv, parent);
View play=findViewById(R.id.play_button);
play.setOutlineProvider(new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
outline.setOval(0, 0, view.getWidth(), view.getHeight());
outline.setAlpha(.99f); // fixes shadow rendering
}
});
}
}
}

View File

@ -0,0 +1,79 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.app.Fragment;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import androidx.annotation.LayoutRes;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
public final int index;
public final int totalPhotos;
protected Attachment attachment;
protected ImageLoaderRequest request;
protected Fragment parentFragment;
protected Status status;
public ImageStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Attachment photo, Status status, int index, int totalPhotos){
super(parentID, parentFragment);
this.attachment=photo;
this.parentFragment=parentFragment;
this.status=status;
this.index=index;
this.totalPhotos=totalPhotos;
}
@Override
public int getImageCount(){
return 1;
}
@Override
public ImageLoaderRequest getImageRequest(int index){
return request;
}
public static abstract class Holder<T extends ImageStatusDisplayItem> extends StatusDisplayItem.Holder<T> implements ImageLoaderViewHolder{
public final ImageView photo;
public Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
super(activity, layout, parent);
photo=findViewById(R.id.photo);
photo.setOnClickListener(this::onViewClick);
}
@Override
public void onBind(ImageStatusDisplayItem item){
}
@Override
public void setImage(int index, Drawable drawable){
photo.setImageDrawable(drawable);
}
@Override
public void clearImage(int index){
photo.setImageDrawable(item.attachment.blurhashPlaceholder);
}
private void onViewClick(View v){
if(item.parentFragment instanceof PhotoViewerHost){
Status contentStatus=item.status.reblog!=null ? item.status.reblog : item.status;
((PhotoViewerHost) item.parentFragment).openPhotoViewer(item.parentID, item.status, contentStatus.mediaAttachments.indexOf(item.attachment));
}
}
}
}

View File

@ -1,37 +1,19 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.app.Fragment;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
public class PhotoStatusDisplayItem extends StatusDisplayItem{
private Attachment attachment;
private ImageLoaderRequest request;
private Fragment parentFragment;
private Status status;
public final int index, totalPhotos;
public class PhotoStatusDisplayItem extends ImageStatusDisplayItem{
public PhotoStatusDisplayItem(String parentID, Status status, Attachment photo, BaseStatusListFragment parentFragment, int index, int totalPhotos){
super(parentID, parentFragment);
this.status=status;
this.attachment=photo;
super(parentID, parentFragment, photo, status, index, totalPhotos);
request=new UrlImageLoaderRequest(photo.url, 1000, 1000);
this.parentFragment=parentFragment;
this.index=index;
this.totalPhotos=totalPhotos;
}
@Override
@ -39,44 +21,10 @@ public class PhotoStatusDisplayItem extends StatusDisplayItem{
return Type.PHOTO;
}
@Override
public int getImageCount(){
return 1;
}
public static class Holder extends ImageStatusDisplayItem.Holder<PhotoStatusDisplayItem>{
@Override
public ImageLoaderRequest getImageRequest(int index){
return request;
}
public static class Holder extends StatusDisplayItem.Holder<PhotoStatusDisplayItem> implements ImageLoaderViewHolder{
public final ImageView photo;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_photo, parent);
photo=findViewById(R.id.photo);
photo.setOnClickListener(this::onViewClick);
}
@Override
public void onBind(PhotoStatusDisplayItem item){
}
@Override
public void setImage(int index, Drawable drawable){
photo.setImageDrawable(drawable);
}
@Override
public void clearImage(int index){
photo.setImageDrawable(item.attachment.blurhashPlaceholder);
}
private void onViewClick(View v){
if(item.parentFragment instanceof PhotoViewerHost){
Status contentStatus=item.status.reblog!=null ? item.status.reblog : item.status;
((PhotoViewerHost) item.parentFragment).openPhotoViewer(item.parentID, item.status, contentStatus.mediaAttachments.indexOf(item.attachment));
}
}
}
}

View File

@ -45,6 +45,7 @@ public abstract class StatusDisplayItem{
case REBLOG_OR_REPLY_LINE -> new ReblogOrReplyLineStatusDisplayItem.Holder(activity, parent);
case TEXT -> new TextStatusDisplayItem.Holder(activity, parent);
case PHOTO -> new PhotoStatusDisplayItem.Holder(activity, parent);
case GIFV -> new GifVStatusDisplayItem.Holder(activity, parent);
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
default -> throw new UnsupportedOperationException();
};
@ -63,7 +64,7 @@ public abstract class StatusDisplayItem{
int photoIndex=0;
int totalPhotos=0;
for(Attachment attachment:statusForContent.mediaAttachments){
if(attachment.type==Attachment.Type.IMAGE){
if(attachment.type==Attachment.Type.IMAGE || attachment.type==Attachment.Type.GIFV){
totalPhotos++;
}
}
@ -71,6 +72,9 @@ public abstract class StatusDisplayItem{
if(attachment.type==Attachment.Type.IMAGE){
items.add(new PhotoStatusDisplayItem(parentID, status, attachment, fragment, photoIndex, totalPhotos));
photoIndex++;
}else if(attachment.type==Attachment.Type.GIFV){
items.add(new GifVStatusDisplayItem(parentID, status, attachment, fragment, photoIndex, totalPhotos));
photoIndex++;
}
}
items.add(new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID));

View File

@ -3,10 +3,16 @@ package org.joinmastodon.android.ui.photoviewer;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
@ -16,6 +22,8 @@ import android.widget.ImageView;
import org.joinmastodon.android.model.Attachment;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
@ -28,6 +36,8 @@ import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.CubicBezierInterpolator;
public class PhotoViewer implements ZoomPanView.Listener{
private static final String TAG="PhotoViewer";
private Activity activity;
private List<Attachment> attachments;
private int currentIndex;
@ -37,6 +47,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
private FrameLayout windowView;
private ViewPager2 pager;
private ColorDrawable background=new ColorDrawable(0xff000000);
private ArrayList<MediaPlayer> players=new ArrayList<>();
public PhotoViewer(Activity activity, List<Attachment> attachments, int index, Listener listener){
this.activity=activity;
@ -79,7 +90,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
int[] radius=new int[4];
if(listener.startPhotoViewTransition(index, rect, radius)){
RecyclerView rv=(RecyclerView) pager.getChildAt(0);
PhotoViewHolder holder=(PhotoViewHolder) rv.findViewHolderForAdapterPosition(index);
BaseHolder holder=(BaseHolder) rv.findViewHolderForAdapterPosition(index);
holder.zoomPanView.animateIn(rect, radius);
}
@ -121,7 +132,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
int[] radius=new int[4];
if(listener.startPhotoViewTransition(index, rect, radius)){
RecyclerView rv=(RecyclerView) pager.getChildAt(0);
PhotoViewHolder holder=(PhotoViewHolder) rv.findViewHolderForAdapterPosition(index);
BaseHolder holder=(BaseHolder) rv.findViewHolderForAdapterPosition(index);
holder.zoomPanView.animateOut(rect, radius, velocityY);
}else{
windowView.animate()
@ -140,6 +151,8 @@ public class PhotoViewer implements ZoomPanView.Listener{
@Override
public void onDismissed(){
for(MediaPlayer player:players)
player.release();
listener.setPhotoViewVisibility(pager.getCurrentItem(), true);
wm.removeView(windowView);
listener.photoViewerDismissed();
@ -193,16 +206,20 @@ public class PhotoViewer implements ZoomPanView.Listener{
void photoViewerDismissed();
}
private class PhotoViewAdapter extends RecyclerView.Adapter<PhotoViewHolder>{
private class PhotoViewAdapter extends RecyclerView.Adapter<BaseHolder>{
@NonNull
@Override
public PhotoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new PhotoViewHolder();
public BaseHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return switch(viewType){
case 0 -> new PhotoViewHolder();
case 1 -> new GifVViewHolder();
default -> throw new IllegalStateException("Unexpected value: "+viewType);
};
}
@Override
public void onBindViewHolder(@NonNull PhotoViewHolder holder, int position){
public void onBindViewHolder(@NonNull BaseHolder holder, int position){
holder.bind(attachments.get(position));
}
@ -210,27 +227,63 @@ public class PhotoViewer implements ZoomPanView.Listener{
public int getItemCount(){
return attachments.size();
}
@Override
public int getItemViewType(int position){
Attachment att=attachments.get(position);
return switch(att.type){
case IMAGE -> 0;
case GIFV -> 1;
default -> throw new IllegalStateException("Unexpected value: "+att.type);
};
}
@Override
public void onViewDetachedFromWindow(@NonNull BaseHolder holder){
super.onViewDetachedFromWindow(holder);
if(holder instanceof GifVViewHolder){
((GifVViewHolder) holder).reset();
}
}
@Override
public void onViewAttachedToWindow(@NonNull BaseHolder holder){
super.onViewAttachedToWindow(holder);
if(holder instanceof GifVViewHolder){
((GifVViewHolder) holder).prepareAndStartPlayer();
}
}
}
private class PhotoViewHolder extends BindableViewHolder<Attachment> implements ViewImageLoader.Target{
public ImageView imageView;
private abstract class BaseHolder extends BindableViewHolder<Attachment>{
public ZoomPanView zoomPanView;
public PhotoViewHolder(){
public BaseHolder(){
super(new ZoomPanView(activity));
zoomPanView=(ZoomPanView) itemView;
zoomPanView.setListener(PhotoViewer.this);
itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
imageView=new ImageView(activity);
((FrameLayout)itemView).addView(imageView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
zoomPanView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
@Override
public void onBind(Attachment item){
zoomPanView.setScrollDirections(getAbsoluteAdapterPosition()>0, getAbsoluteAdapterPosition()<attachments.size()-1);
}
}
private class PhotoViewHolder extends BaseHolder implements ViewImageLoader.Target{
public ImageView imageView;
public PhotoViewHolder(){
imageView=new ImageView(activity);
zoomPanView.addView(imageView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
}
@Override
public void onBind(Attachment item){
super.onBind(item);
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) imageView.getLayoutParams();
params.width=item.getWidth();
params.height=item.getHeight();
zoomPanView.setScrollDirections(getAbsoluteAdapterPosition()>0, getAbsoluteAdapterPosition()<attachments.size()-1);
ViewImageLoader.load(this, listener.getPhotoViewCurrentDrawable(getAbsoluteAdapterPosition()), new UrlImageLoaderRequest(item.url), false);
}
@ -244,4 +297,98 @@ public class PhotoViewer implements ZoomPanView.Listener{
return imageView;
}
}
private class GifVViewHolder extends BaseHolder implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, TextureView.SurfaceTextureListener{
public TextureView textureView;
public FrameLayout wrap;
public MediaPlayer player;
private Surface surface;
private boolean playerReady;
public GifVViewHolder(){
textureView=new TextureView(activity);
wrap=new FrameLayout(activity);
zoomPanView.addView(wrap, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
wrap.addView(textureView);
textureView.setSurfaceTextureListener(this);
}
@Override
public void onBind(Attachment item){
super.onBind(item);
playerReady=false;
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) wrap.getLayoutParams();
params.width=item.getWidth();
params.height=item.getHeight();
wrap.setBackground(listener.getPhotoViewCurrentDrawable(getAbsoluteAdapterPosition()));
if(itemView.isAttachedToWindow()){
prepareAndStartPlayer();
}
}
@Override
public void onPrepared(MediaPlayer mp){
Log.d(TAG, "onPrepared() called with: mp = ["+mp+"]");
playerReady=true;
if(surface!=null)
startPlayer();
}
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height){
this.surface=new Surface(surface);
if(playerReady)
startPlayer();
}
@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height){
}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface){
this.surface=null;
return true;
}
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface){
}
private void startPlayer(){
player.setSurface(surface);
player.setLooping(true);
player.start();
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra){
Log.e(TAG, "gif player onError() called with: mp = ["+mp+"], what = ["+what+"], extra = ["+extra+"]");
return false;
}
public void prepareAndStartPlayer(){
playerReady=false;
player=new MediaPlayer();
players.add(player);
player.setOnPreparedListener(this);
player.setOnErrorListener(this);
try{
player.setDataSource(activity, Uri.parse(item.url));
player.prepareAsync();
}catch(IOException x){
Log.w(TAG, "Error initializing gif player", x);
}
}
public void reset(){
playerReady=false;
player.release();
players.remove(player);
player=null;
}
}
}

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<path
android:pathData="M21.875,4.084C23.9691,4.084 25.6667,5.7816 25.6667,7.8756V20.1287C25.6667,22.2227 23.9691,23.9203 21.875,23.9203H6.125C4.0309,23.9203 2.3333,22.2227 2.3333,20.1287V7.8756C2.3333,5.7816 4.0309,4.084 6.125,4.084H21.875ZM9.3504,10.3506C7.4531,10.3506 6.1379,11.9953 6.1379,13.9934C6.1379,15.9922 7.4534,17.6403 9.3504,17.6403C10.3861,17.6403 11.3373,17.1421 11.814,16.2241L11.9029,16.0349L11.9303,15.9537L11.9514,15.8666L11.9602,15.7698L11.9605,13.9956L11.9526,13.8878C11.9063,13.5753 11.6617,13.3274 11.3507,13.2761L11.2313,13.2664H10.0628L9.955,13.2743C9.6425,13.3206 9.3946,13.5652 9.3433,13.8761L9.3336,13.9956L9.3415,14.1033C9.3878,14.4159 9.6324,14.6638 9.9433,14.715L10.0628,14.7247L10.5012,14.7244V15.5808L10.494,15.5978C10.2738,15.9722 9.8504,16.182 9.3504,16.182C8.3402,16.182 7.5962,15.2499 7.5962,13.9934C7.5962,12.738 8.3392,11.8089 9.3504,11.8089C9.842,11.8089 10.1218,11.8629 10.4716,12.0308C10.8346,12.2052 11.2702,12.0522 11.4446,11.6892C11.6189,11.3262 11.4659,10.8906 11.1029,10.7162C10.5391,10.4455 10.0468,10.3506 9.3504,10.3506ZM14.7338,10.4929C14.3677,10.4929 14.0646,10.7627 14.0125,11.1144L14.0046,11.2221V16.7779L14.0125,16.8857C14.0646,17.2373 14.3677,17.5071 14.7338,17.5071C15.0999,17.5071 15.403,17.2373 15.455,16.8857L15.4629,16.7779V11.2221L15.455,11.1144C15.403,10.7627 15.0999,10.4929 14.7338,10.4929ZM20.5592,10.501L18.2289,10.4929C17.8628,10.4917 17.5588,10.7604 17.5055,11.1119L17.4972,11.2196V16.7592L17.5051,16.8669C17.5514,17.1794 17.796,17.4274 18.1069,17.4786L18.2264,17.4883L18.3341,17.4804C18.6467,17.4341 18.8946,17.1895 18.9458,16.8786L18.9555,16.7592L18.9549,15.4583L20.265,15.4588L20.3728,15.4509C20.6853,15.4046 20.9332,15.16 20.9844,14.8491L20.9942,14.7297L20.9863,14.6219C20.94,14.3094 20.6954,14.0615 20.3845,14.0102L20.265,14.0005L18.9549,13.9999V11.9524L20.5542,11.9593L20.6619,11.9518C21.0137,11.9009 21.2846,11.5988 21.2859,11.2327C21.2871,10.8666 21.0184,10.5626 20.6669,10.5093L20.5592,10.501L18.2289,10.4929L20.5592,10.501Z"
android:fillColor="#000000"
android:fillAlpha="0.8"/>
<path
android:pathData="M6.1379,13.9933C6.1379,11.9953 7.4531,10.3506 9.3504,10.3506C10.0468,10.3506 10.5391,10.4455 11.1029,10.7162C11.4659,10.8905 11.6189,11.3261 11.4446,11.6892C11.2702,12.0522 10.8346,12.2051 10.4716,12.0308C10.1218,11.8628 9.842,11.8089 9.3504,11.8089C8.3392,11.8089 7.5962,12.738 7.5962,13.9933C7.5962,15.2498 8.3402,16.182 9.3504,16.182C9.8504,16.182 10.2738,15.9721 10.494,15.5977L10.5012,15.5808V14.7244L10.0628,14.7247L9.9433,14.715C9.6324,14.6638 9.3878,14.4158 9.3415,14.1033L9.3336,13.9956L9.3433,13.8761C9.3946,13.5652 9.6425,13.3206 9.955,13.2743L10.0628,13.2664H11.2313L11.3507,13.2761C11.6616,13.3274 11.9063,13.5753 11.9526,13.8878L11.9605,13.9956L11.9602,15.7697L11.9514,15.8666L11.9303,15.9536L11.9029,16.0349L11.814,16.2241C11.3373,17.142 10.3861,17.6403 9.3504,17.6403C7.4533,17.6403 6.1379,15.9922 6.1379,13.9933ZM14.0125,11.1143C14.0646,10.7627 14.3677,10.4929 14.7338,10.4929C15.0999,10.4929 15.403,10.7627 15.455,11.1143L15.4629,11.2221V16.7779L15.455,16.8857C15.403,17.2373 15.0999,17.5071 14.7338,17.5071C14.3677,17.5071 14.0646,17.2373 14.0125,16.8857L14.0046,16.7779V11.2221L14.0125,11.1143ZM18.2289,10.4929L20.5592,10.501L20.6669,10.5092C21.0183,10.5625 21.2871,10.8666 21.2858,11.2326C21.2846,11.5987 21.0137,11.9009 20.6619,11.9518L20.5542,11.9593L18.9549,11.9524V13.9999L20.265,14.0005L20.3844,14.0102C20.6954,14.0614 20.94,14.3093 20.9863,14.6219L20.9942,14.7296L20.9844,14.8491C20.9332,15.16 20.6853,15.4046 20.3728,15.4509L20.265,15.4588L18.9549,15.4583L18.9555,16.7591L18.9458,16.8786C18.8946,17.1895 18.6467,17.4341 18.3341,17.4804L18.2264,17.4883L18.1069,17.4786C17.796,17.4273 17.5514,17.1794 17.5051,16.8669L17.4972,16.7591V11.2196L17.5055,11.1118C17.5588,10.7604 17.8628,10.4917 18.2289,10.4929Z"
android:fillColor="#FCFCFD" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M3,3.2739C3,1.567 4.8261,0.4817 6.3254,1.2975L18.687,8.0236C20.2531,8.8758 20.2531,11.1242 18.687,11.9764L6.3254,18.7025C4.8261,19.5183 3,18.433 3,16.7262V3.2739Z"
android:fillColor="#F9FAFB"/>
</vector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<stroke android:color="@color/gray_50" android:width="4dp"/>
<solid android:color="#80000000"/>
<size android:width="52dp" android:height="52dp"/>
</shape>
</item>
<item android:drawable="@drawable/ic_play" android:gravity="center"/>
</layer-list>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/photo"
android:layout_width="match_parent"
android:layout_height="250dp"
android:layout_gravity="center"
android:scaleType="centerCrop"/>
<View
android:id="@+id/play_button"
android:layout_width="52dp"
android:layout_height="52dp"
android:layout_gravity="center"
android:elevation="3dp"
android:background="@drawable/play_button"/>
<View
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_gravity="bottom|end"
android:layout_margin="8dp"
android:background="@drawable/ic_gif"/>
</FrameLayout>