Fix several Gif MP4 UX issues.

This commit is contained in:
Alex Hart 2021-06-09 10:23:41 -03:00
parent 2029ea378f
commit 335ff61011
11 changed files with 75 additions and 50 deletions

View file

@ -103,6 +103,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
public static final String HIDE_ALL_MEDIA_EXTRA = "came_from_all_media"; public static final String HIDE_ALL_MEDIA_EXTRA = "came_from_all_media";
public static final String SHOW_THREAD_EXTRA = "show_thread"; public static final String SHOW_THREAD_EXTRA = "show_thread";
public static final String SORTING_EXTRA = "sorting"; public static final String SORTING_EXTRA = "sorting";
public static final String IS_VIDEO_GIF = "is_video_gif";
private ViewPager mediaPager; private ViewPager mediaPager;
private View detailsContainer; private View detailsContainer;
@ -115,6 +116,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private String initialMediaType; private String initialMediaType;
private long initialMediaSize; private long initialMediaSize;
private String initialCaption; private String initialCaption;
private boolean initialMediaIsVideoGif;
private boolean leftIsRecent; private boolean leftIsRecent;
private MediaPreviewViewModel viewModel; private MediaPreviewViewModel viewModel;
private ViewPagerListener viewPagerListener; private ViewPagerListener viewPagerListener;
@ -139,6 +141,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize()); intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, attachment.getSize());
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption()); intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, attachment.getCaption());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent); intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, leftIsRecent);
intent.putExtra(MediaPreviewActivity.IS_VIDEO_GIF, attachment.isVideoGif());
intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType()); intent.setDataAndType(attachment.getUri(), mediaRecord.getContentType());
return intent; return intent;
} }
@ -296,12 +299,13 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
showThread = intent.getBooleanExtra(SHOW_THREAD_EXTRA, false); showThread = intent.getBooleanExtra(SHOW_THREAD_EXTRA, false);
sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(SORTING_EXTRA, 0)]; sorting = MediaDatabase.Sorting.values()[intent.getIntExtra(SORTING_EXTRA, 0)];
initialMediaUri = intent.getData(); initialMediaUri = intent.getData();
initialMediaType = intent.getType(); initialMediaType = intent.getType();
initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0); initialMediaSize = intent.getLongExtra(SIZE_EXTRA, 0);
initialCaption = intent.getStringExtra(CAPTION_EXTRA); initialCaption = intent.getStringExtra(CAPTION_EXTRA);
leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false); leftIsRecent = intent.getBooleanExtra(LEFT_IS_RECENT_EXTRA, false);
restartItem = -1; initialMediaIsVideoGif = intent.getBooleanExtra(IS_VIDEO_GIF, false);
restartItem = -1;
} }
private void initializeObservers() { private void initializeObservers() {
@ -354,7 +358,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
if (isMediaInDb()) { if (isMediaInDb()) {
LoaderManager.getInstance(this).restartLoader(0, null, this); LoaderManager.getInstance(this).restartLoader(0, null, this);
} else { } else {
mediaPager.setAdapter(new SingleItemPagerAdapter(getSupportFragmentManager(), initialMediaUri, initialMediaType, initialMediaSize)); mediaPager.setAdapter(new SingleItemPagerAdapter(getSupportFragmentManager(), initialMediaUri, initialMediaType, initialMediaSize, initialMediaIsVideoGif));
if (initialCaption != null) { if (initialCaption != null) {
detailsContainer.setVisibility(View.VISIBLE); detailsContainer.setVisibility(View.VISIBLE);
@ -632,21 +636,24 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
private static class SingleItemPagerAdapter extends FragmentStatePagerAdapter implements MediaItemAdapter { private static class SingleItemPagerAdapter extends FragmentStatePagerAdapter implements MediaItemAdapter {
private final Uri uri; private final Uri uri;
private final String mediaType; private final String mediaType;
private final long size; private final long size;
private final boolean isVideoGif;
private MediaPreviewFragment mediaPreviewFragment; private MediaPreviewFragment mediaPreviewFragment;
SingleItemPagerAdapter(@NonNull FragmentManager fragmentManager, SingleItemPagerAdapter(@NonNull FragmentManager fragmentManager,
@NonNull Uri uri, @NonNull Uri uri,
@NonNull String mediaType, @NonNull String mediaType,
long size) long size,
boolean isVideoGif)
{ {
super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); super(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.uri = uri; this.uri = uri;
this.mediaType = mediaType; this.mediaType = mediaType;
this.size = size; this.size = size;
this.isVideoGif = isVideoGif;
} }
@Override @Override
@ -657,7 +664,7 @@ public final class MediaPreviewActivity extends PassphraseRequiredActivity
@NonNull @NonNull
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
mediaPreviewFragment = MediaPreviewFragment.newInstance(uri, mediaType, size, true); mediaPreviewFragment = MediaPreviewFragment.newInstance(uri, mediaType, size, true, isVideoGif);
return mediaPreviewFragment; return mediaPreviewFragment;
} }

View file

@ -25,6 +25,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.blurhash.BlurHash; import org.thoughtcrime.securesms.blurhash.BlurHash;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4PlaybackPolicy;
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri;
import org.thoughtcrime.securesms.mms.GlideRequest; import org.thoughtcrime.securesms.mms.GlideRequest;
import org.thoughtcrime.securesms.mms.GlideRequests; import org.thoughtcrime.securesms.mms.GlideRequests;
@ -61,7 +62,6 @@ public class ThumbnailView extends FrameLayout {
private ImageView blurhash; private ImageView blurhash;
private View playOverlay; private View playOverlay;
private View captionIcon; private View captionIcon;
private Stub<VideoPlayer> videoPlayer;
private OnClickListener parentClickListener; private OnClickListener parentClickListener;
private final int[] dimens = new int[2]; private final int[] dimens = new int[2];
@ -93,7 +93,6 @@ public class ThumbnailView extends FrameLayout {
this.blurhash = findViewById(R.id.thumbnail_blurhash); this.blurhash = findViewById(R.id.thumbnail_blurhash);
this.playOverlay = findViewById(R.id.play_overlay); this.playOverlay = findViewById(R.id.play_overlay);
this.captionIcon = findViewById(R.id.thumbnail_caption_icon); this.captionIcon = findViewById(R.id.thumbnail_caption_icon);
this.videoPlayer = new Stub<>(findViewById(R.id.thumbnail_player_stub));
super.setOnClickListener(new ThumbnailClickDispatcher()); super.setOnClickListener(new ThumbnailClickDispatcher());
if (attrs != null) { if (attrs != null) {

View file

@ -1734,6 +1734,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp()); intent.putExtra(MediaPreviewActivity.DATE_EXTRA, messageRecord.getTimestamp());
intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize()); intent.putExtra(MediaPreviewActivity.SIZE_EXTRA, slide.asAttachment().getSize());
intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull()); intent.putExtra(MediaPreviewActivity.CAPTION_EXTRA, slide.getCaption().orNull());
intent.putExtra(MediaPreviewActivity.IS_VIDEO_GIF, slide.isVideoGif());
intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false); intent.putExtra(MediaPreviewActivity.LEFT_IS_RECENT_EXTRA, false);
context.startActivity(intent); context.startActivity(intent);

View file

@ -236,6 +236,7 @@ public final class MediaOverviewPageFragment extends Fragment
intent.putExtra(MediaPreviewActivity.HIDE_ALL_MEDIA_EXTRA, true); intent.putExtra(MediaPreviewActivity.HIDE_ALL_MEDIA_EXTRA, true);
intent.putExtra(MediaPreviewActivity.SHOW_THREAD_EXTRA, threadId == MediaDatabase.ALL_THREADS); intent.putExtra(MediaPreviewActivity.SHOW_THREAD_EXTRA, threadId == MediaDatabase.ALL_THREADS);
intent.putExtra(MediaPreviewActivity.SORTING_EXTRA, sorting.ordinal()); intent.putExtra(MediaPreviewActivity.SORTING_EXTRA, sorting.ordinal());
intent.putExtra(MediaPreviewActivity.IS_VIDEO_GIF, attachment.isVideoGif());
intent.setDataAndType(mediaRecord.getAttachment().getUri(), mediaRecord.getContentType()); intent.setDataAndType(mediaRecord.getAttachment().getUri(), mediaRecord.getContentType());
context.startActivity(intent); context.startActivity(intent);

View file

@ -24,21 +24,23 @@ public abstract class MediaPreviewFragment extends Fragment {
static final String DATA_SIZE = "DATA_SIZE"; static final String DATA_SIZE = "DATA_SIZE";
static final String DATA_CONTENT_TYPE = "DATA_CONTENT_TYPE"; static final String DATA_CONTENT_TYPE = "DATA_CONTENT_TYPE";
static final String AUTO_PLAY = "AUTO_PLAY"; static final String AUTO_PLAY = "AUTO_PLAY";
static final String VIDEO_GIF = "VIDEO_GIF";
private AttachmentId attachmentId; private AttachmentId attachmentId;
protected Events events; protected Events events;
public static MediaPreviewFragment newInstance(@NonNull Attachment attachment, boolean autoPlay) { public static MediaPreviewFragment newInstance(@NonNull Attachment attachment, boolean autoPlay) {
return newInstance(attachment.getUri(), attachment.getContentType(), attachment.getSize(), autoPlay); return newInstance(attachment.getUri(), attachment.getContentType(), attachment.getSize(), autoPlay, attachment.isVideoGif());
} }
public static MediaPreviewFragment newInstance(@NonNull Uri dataUri, @NonNull String contentType, long size, boolean autoPlay) { public static MediaPreviewFragment newInstance(@NonNull Uri dataUri, @NonNull String contentType, long size, boolean autoPlay, boolean isVideoGif) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(MediaPreviewFragment.DATA_URI, dataUri); args.putParcelable(MediaPreviewFragment.DATA_URI, dataUri);
args.putString(MediaPreviewFragment.DATA_CONTENT_TYPE, contentType); args.putString(MediaPreviewFragment.DATA_CONTENT_TYPE, contentType);
args.putLong(MediaPreviewFragment.DATA_SIZE, size); args.putLong(MediaPreviewFragment.DATA_SIZE, size);
args.putBoolean(MediaPreviewFragment.AUTO_PLAY, autoPlay); args.putBoolean(MediaPreviewFragment.AUTO_PLAY, autoPlay);
args.putBoolean(MediaPreviewFragment.VIDEO_GIF, isVideoGif);
MediaPreviewFragment fragment = createCorrectFragmentType(contentType); MediaPreviewFragment fragment = createCorrectFragmentType(contentType);

View file

@ -19,6 +19,7 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
private static final String TAG = Log.tag(VideoMediaPreviewFragment.class); private static final String TAG = Log.tag(VideoMediaPreviewFragment.class);
private VideoPlayer videoView; private VideoPlayer videoView;
private boolean isVideoGif;
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
@ -35,6 +36,8 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
long size = arguments.getLong(DATA_SIZE); long size = arguments.getLong(DATA_SIZE);
boolean autoPlay = arguments.getBoolean(AUTO_PLAY); boolean autoPlay = arguments.getBoolean(AUTO_PLAY);
isVideoGif = arguments.getBoolean(VIDEO_GIF);
if (!MediaUtil.isVideo(contentType)) { if (!MediaUtil.isVideo(contentType)) {
throw new AssertionError("This fragment can only display video"); throw new AssertionError("This fragment can only display video");
} }
@ -44,6 +47,11 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
videoView.setWindow(requireActivity().getWindow()); videoView.setWindow(requireActivity().getWindow());
videoView.setVideoSource(new VideoSlide(getContext(), uri, size, false), autoPlay); videoView.setVideoSource(new VideoSlide(getContext(), uri, size, false), autoPlay);
if (isVideoGif) {
videoView.hideControls();
videoView.loopForever();
}
videoView.setOnClickListener(v -> events.singleTapOnMedia()); videoView.setOnClickListener(v -> events.singleTapOnMedia());
return itemView; return itemView;
@ -56,6 +64,14 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
} }
} }
@Override
public void onResume() {
super.onResume();
if (videoView != null && isVideoGif) {
videoView.play();
}
}
@Override @Override
public void pause() { public void pause() {
if (videoView != null) { if (videoView != null) {
@ -65,6 +81,6 @@ public final class VideoMediaPreviewFragment extends MediaPreviewFragment {
@Override @Override
public View getPlaybackControls() { public View getPlaybackControls() {
return videoView != null ? videoView.getControlView() : null; return videoView != null && !isVideoGif ? videoView.getControlView() : null;
} }
} }

View file

@ -30,7 +30,7 @@ class MediaSendFragmentPagerAdapter extends FragmentStatePagerAdapter {
private final MediaConstraints mediaConstraints; private final MediaConstraints mediaConstraints;
MediaSendFragmentPagerAdapter(@NonNull FragmentManager fm, @NonNull MediaConstraints mediaConstraints) { MediaSendFragmentPagerAdapter(@NonNull FragmentManager fm, @NonNull MediaConstraints mediaConstraints) {
super(fm); super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.mediaConstraints = mediaConstraints; this.mediaConstraints = mediaConstraints;
this.media = new ArrayList<>(); this.media = new ArrayList<>();
this.fragments = new HashMap<>(); this.fragments = new HashMap<>();
@ -48,7 +48,8 @@ class MediaSendFragmentPagerAdapter extends FragmentStatePagerAdapter {
} else if (MediaUtil.isVideoType(mediaItem.getMimeType())) { } else if (MediaUtil.isVideoType(mediaItem.getMimeType())) {
return MediaSendVideoFragment.newInstance(mediaItem.getUri(), return MediaSendVideoFragment.newInstance(mediaItem.getUri(),
mediaConstraints.getCompressedVideoMaxSize(ApplicationDependencies.getApplication()), mediaConstraints.getCompressedVideoMaxSize(ApplicationDependencies.getApplication()),
mediaConstraints.getVideoMaxSize(ApplicationDependencies.getApplication())); mediaConstraints.getVideoMaxSize(ApplicationDependencies.getApplication()),
mediaItem.isVideoGif());
} else { } else {
throw new UnsupportedOperationException("Can only render images and videos. Found mimetype: '" + mediaItem.getMimeType() + "'"); throw new UnsupportedOperationException("Can only render images and videos. Found mimetype: '" + mediaItem.getMimeType() + "'");
} }

View file

@ -30,9 +30,10 @@ public class MediaSendVideoFragment extends Fragment implements VideoEditorHud.E
private static final String TAG = Log.tag(MediaSendVideoFragment.class); private static final String TAG = Log.tag(MediaSendVideoFragment.class);
private static final String KEY_URI = "uri"; private static final String KEY_URI = "uri";
private static final String KEY_MAX_OUTPUT = "max_output_size"; private static final String KEY_MAX_OUTPUT = "max_output_size";
private static final String KEY_MAX_SEND = "max_send_size"; private static final String KEY_MAX_SEND = "max_send_size";
private static final String KEY_IS_VIDEO_GIF = "is_video_gif";
private final Throttler videoScanThrottle = new Throttler(150); private final Throttler videoScanThrottle = new Throttler(150);
private final Handler handler = new Handler(Looper.getMainLooper()); private final Handler handler = new Handler(Looper.getMainLooper());
@ -40,15 +41,17 @@ public class MediaSendVideoFragment extends Fragment implements VideoEditorHud.E
private Controller controller; private Controller controller;
private Data data = new Data(); private Data data = new Data();
private Uri uri; private Uri uri;
private boolean isVideoGif;
private VideoPlayer player; private VideoPlayer player;
@Nullable private VideoEditorHud hud; @Nullable private VideoEditorHud hud;
private Runnable updatePosition; private Runnable updatePosition;
public static MediaSendVideoFragment newInstance(@NonNull Uri uri, long maxCompressedVideoSize, long maxAttachmentSize) { public static MediaSendVideoFragment newInstance(@NonNull Uri uri, long maxCompressedVideoSize, long maxAttachmentSize, boolean isVideoGif) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(KEY_URI, uri); args.putParcelable(KEY_URI, uri);
args.putLong(KEY_MAX_OUTPUT, maxCompressedVideoSize); args.putLong(KEY_MAX_OUTPUT, maxCompressedVideoSize);
args.putLong(KEY_MAX_SEND, maxAttachmentSize); args.putLong(KEY_MAX_SEND, maxAttachmentSize);
args.putBoolean(KEY_IS_VIDEO_GIF, isVideoGif);
MediaSendVideoFragment fragment = new MediaSendVideoFragment(); MediaSendVideoFragment fragment = new MediaSendVideoFragment();
fragment.setArguments(args); fragment.setArguments(args);
@ -76,15 +79,20 @@ public class MediaSendVideoFragment extends Fragment implements VideoEditorHud.E
player = view.findViewById(R.id.video_player); player = view.findViewById(R.id.video_player);
uri = requireArguments().getParcelable(KEY_URI); uri = requireArguments().getParcelable(KEY_URI);
long maxOutput = requireArguments().getLong(KEY_MAX_OUTPUT); isVideoGif = requireArguments().getBoolean(KEY_IS_VIDEO_GIF);
long maxSend = requireArguments().getLong(KEY_MAX_SEND);
VideoSlide slide = new VideoSlide(requireContext(), uri, 0, false); long maxOutput = requireArguments().getLong(KEY_MAX_OUTPUT);
long maxSend = requireArguments().getLong(KEY_MAX_SEND);
VideoSlide slide = new VideoSlide(requireContext(), uri, 0, isVideoGif);
player.setWindow(requireActivity().getWindow()); player.setWindow(requireActivity().getWindow());
player.setVideoSource(slide, true); player.setVideoSource(slide, true);
if (MediaConstraints.isVideoTranscodeAvailable()) { if (slide.isVideoGif()) {
player.hideControls();
player.loopForever();
} else if (MediaConstraints.isVideoTranscodeAvailable()) {
hud = view.findViewById(R.id.video_editor_hud); hud = view.findViewById(R.id.video_editor_hud);
hud.setEventListener(this); hud.setEventListener(this);
updateHud(data); updateHud(data);
@ -140,6 +148,10 @@ public class MediaSendVideoFragment extends Fragment implements VideoEditorHud.E
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
startPositionUpdates(); startPositionUpdates();
if (player != null && isVideoGif) {
player.play();
}
} }
private void startPositionUpdates() { private void startPositionUpdates() {
@ -180,8 +192,9 @@ public class MediaSendVideoFragment extends Fragment implements VideoEditorHud.E
@Override @Override
public @Nullable View getPlaybackControls() { public @Nullable View getPlaybackControls() {
if (hud != null && hud.getVisibility() == View.VISIBLE) return null; if (hud != null && hud.getVisibility() == View.VISIBLE) return null;
else if (isVideoGif) return null;
return player != null ? player.getControlView() : null; else if (player != null) return player.getControlView();
else return null;
} }
@Override @Override

View file

@ -27,6 +27,7 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.giph.mp4.GiphyMp4PlaybackPolicy;
import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.MediaUtil;
public class VideoSlide extends Slide { public class VideoSlide extends Slide {
@ -54,7 +55,7 @@ public class VideoSlide extends Slide {
@Override @Override
public boolean hasPlayOverlay() { public boolean hasPlayOverlay() {
return true; return !(isVideoGif() && GiphyMp4PlaybackPolicy.autoplay());
} }
@Override @Override

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.video.VideoPlayer xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:contentDescription="@string/conversation_item__mms_image_description"
android:longClickable="false" />

View file

@ -24,15 +24,6 @@
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:contentDescription="@string/conversation_item__mms_image_description" /> android:contentDescription="@string/conversation_item__mms_image_description" />
<ViewStub
android:id="@+id/thumbnail_player_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:inflatedId="@id/thumbnail_player_stub"
android:layout="@layout/thumbnail_player_stub"
android:longClickable="false" />
<ImageView <ImageView
android:id="@+id/thumbnail_caption_icon" android:id="@+id/thumbnail_caption_icon"
android:layout_width="wrap_content" android:layout_width="wrap_content"