mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 00:29:11 +01:00
committed by
Greyson Parrelli
parent
d507df2e7e
commit
a6690e1bde
@@ -23,15 +23,13 @@ import java.util.Objects;
|
||||
final class GiphyMp4Adapter extends ListAdapter<GiphyImage, GiphyMp4ViewHolder> {
|
||||
|
||||
private final Callback listener;
|
||||
private final GiphyMp4MediaSourceFactory mediaSourceFactory;
|
||||
|
||||
private PagingController pagingController;
|
||||
|
||||
public GiphyMp4Adapter(@NonNull GiphyMp4MediaSourceFactory mediaSourceFactory, @Nullable Callback listener) {
|
||||
public GiphyMp4Adapter(@Nullable Callback listener) {
|
||||
super(new GiphyImageDiffUtilCallback());
|
||||
|
||||
this.listener = listener;
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,7 +37,7 @@ final class GiphyMp4Adapter extends ListAdapter<GiphyImage, GiphyMp4ViewHolder>
|
||||
View itemView = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.giphy_mp4, parent, false);
|
||||
|
||||
return new GiphyMp4ViewHolder(itemView, listener, mediaSourceFactory);
|
||||
return new GiphyMp4ViewHolder(itemView, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,39 +6,37 @@ import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
|
||||
import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.LoadControl;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.source.MediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.net.ContentProxySelector;
|
||||
import org.thoughtcrime.securesms.video.exo.ChunkedDataSourceFactory;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
/**
|
||||
* Provider which creates ExoPlayer instances for displaying Giphy content.
|
||||
*/
|
||||
final class GiphyMp4ExoPlayerProvider implements DefaultLifecycleObserver {
|
||||
|
||||
private final Context context;
|
||||
private final TrackSelection.Factory videoTrackSelectionFactory;
|
||||
private final DefaultRenderersFactory renderersFactory;
|
||||
private final TrackSelector trackSelector;
|
||||
private final LoadControl loadControl;
|
||||
private final Context context;
|
||||
private final OkHttpClient okHttpClient = ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(new ContentProxySelector()).build();
|
||||
private final DataSource.Factory dataSourceFactory = new ChunkedDataSourceFactory(okHttpClient, null);
|
||||
private final MediaSourceFactory mediaSourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
|
||||
|
||||
GiphyMp4ExoPlayerProvider(@NonNull Context context) {
|
||||
this.context = context;
|
||||
this.videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
this.renderersFactory = new DefaultRenderersFactory(context);
|
||||
this.trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
this.loadControl = new DefaultLoadControl();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@MainThread final @NonNull ExoPlayer create() {
|
||||
SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(context, renderersFactory, trackSelector, loadControl);
|
||||
SimpleExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context)
|
||||
.setMediaSourceFactory(mediaSourceFactory)
|
||||
.build();
|
||||
|
||||
exoPlayer.setRepeatMode(Player.REPEAT_MODE_ALL);
|
||||
exoPlayer.setVolume(0f);
|
||||
|
||||
@@ -14,8 +14,6 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.net.ContentProxySelector;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -48,8 +46,7 @@ public class GiphyMp4Fragment extends Fragment {
|
||||
ContentLoadingProgressBar progressBar = view.findViewById(R.id.content_loading);
|
||||
TextView nothingFound = view.findViewById(R.id.nothing_found);
|
||||
GiphyMp4ViewModel viewModel = ViewModelProviders.of(requireActivity(), new GiphyMp4ViewModel.Factory(isForMms)).get(GiphyMp4ViewModel.class);
|
||||
GiphyMp4MediaSourceFactory mediaSourceFactory = new GiphyMp4MediaSourceFactory(ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(new ContentProxySelector()).build());
|
||||
GiphyMp4Adapter adapter = new GiphyMp4Adapter(mediaSourceFactory, viewModel::saveToBlob);
|
||||
GiphyMp4Adapter adapter = new GiphyMp4Adapter(viewModel::saveToBlob);
|
||||
List<GiphyMp4ProjectionPlayerHolder> holders = GiphyMp4ProjectionPlayerHolder.injectVideoViews(requireContext(),
|
||||
getViewLifecycleOwner().getLifecycle(),
|
||||
frameLayout,
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.thoughtcrime.securesms.giph.mp4;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
||||
import org.thoughtcrime.securesms.video.exo.ChunkedDataSourceFactory;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
/**
|
||||
* Factory which creates MediaSource objects for given Giphy URIs
|
||||
*/
|
||||
final class GiphyMp4MediaSourceFactory {
|
||||
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final ExtractorsFactory extractorsFactory;
|
||||
private final ExtractorMediaSource.Factory extractorMediaSourceFactory;
|
||||
|
||||
GiphyMp4MediaSourceFactory(@NonNull OkHttpClient okHttpClient) {
|
||||
dataSourceFactory = new ChunkedDataSourceFactory(okHttpClient, null);
|
||||
extractorsFactory = new DefaultExtractorsFactory();
|
||||
extractorMediaSourceFactory = new ExtractorMediaSource.Factory(dataSourceFactory).setExtractorsFactory(extractorsFactory);
|
||||
}
|
||||
|
||||
@NonNull MediaSource create(@NonNull Uri uri) {
|
||||
return extractorMediaSourceFactory.createMediaSource(uri);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
|
||||
import org.thoughtcrime.securesms.util.Projection;
|
||||
|
||||
@@ -23,9 +23,9 @@ public interface GiphyMp4Playable {
|
||||
void hideProjectionArea();
|
||||
|
||||
/**
|
||||
* @return The MediaSource to play back in the given VideoPlayer
|
||||
* @return The MediaItem to play back in the given VideoPlayer
|
||||
*/
|
||||
default @Nullable MediaSource getMediaSource() {
|
||||
default @Nullable MediaItem getMediaItem() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public final class GiphyMp4PlaybackPolicy {
|
||||
int maxInstances = 0;
|
||||
|
||||
try {
|
||||
MediaCodecInfo info = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264, false);
|
||||
MediaCodecInfo info = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264, false, false);
|
||||
|
||||
if (info != null && info.getMaxSupportedInstances() > 0) {
|
||||
maxInstances = (int) (info.getMaxSupportedInstances() * ratio);
|
||||
|
||||
@@ -11,13 +11,11 @@ import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
|
||||
import org.signal.glide.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.CornerMask;
|
||||
import org.thoughtcrime.securesms.util.Projection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -26,12 +24,12 @@ import java.util.List;
|
||||
/**
|
||||
* Object which holds on to an injected video player.
|
||||
*/
|
||||
public final class GiphyMp4ProjectionPlayerHolder implements Player.EventListener {
|
||||
public final class GiphyMp4ProjectionPlayerHolder implements Player.Listener {
|
||||
private final FrameLayout container;
|
||||
private final GiphyMp4VideoPlayer player;
|
||||
|
||||
private Runnable onPlaybackReady;
|
||||
private MediaSource mediaSource;
|
||||
private MediaItem mediaItem;
|
||||
private GiphyMp4PlaybackPolicyEnforcer policyEnforcer;
|
||||
|
||||
private GiphyMp4ProjectionPlayerHolder(@NonNull FrameLayout container, @NonNull GiphyMp4VideoPlayer player) {
|
||||
@@ -43,22 +41,22 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.EventListene
|
||||
return container;
|
||||
}
|
||||
|
||||
public void playContent(@NonNull MediaSource mediaSource, @Nullable GiphyMp4PlaybackPolicyEnforcer policyEnforcer) {
|
||||
this.mediaSource = mediaSource;
|
||||
public void playContent(@NonNull MediaItem mediaItem, @Nullable GiphyMp4PlaybackPolicyEnforcer policyEnforcer) {
|
||||
this.mediaItem = mediaItem;
|
||||
this.policyEnforcer = policyEnforcer;
|
||||
|
||||
player.setVideoSource(mediaSource);
|
||||
player.setVideoItem(mediaItem);
|
||||
player.play();
|
||||
}
|
||||
|
||||
public void clearMedia() {
|
||||
this.mediaSource = null;
|
||||
this.mediaItem = null;
|
||||
this.policyEnforcer = null;
|
||||
player.stop();
|
||||
}
|
||||
|
||||
public @Nullable MediaSource getMediaSource() {
|
||||
return mediaSource;
|
||||
public @Nullable MediaItem getMediaItem() {
|
||||
return mediaItem;
|
||||
}
|
||||
|
||||
public void setOnPlaybackReady(@Nullable Runnable onPlaybackReady) {
|
||||
@@ -74,7 +72,7 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.EventListene
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
public void onPlaybackStateChanged(int playbackState) {
|
||||
if (playbackState == Player.STATE_READY) {
|
||||
if (onPlaybackReady != null) {
|
||||
if (policyEnforcer != null) {
|
||||
@@ -86,8 +84,11 @@ public final class GiphyMp4ProjectionPlayerHolder implements Player.EventListene
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(int reason) {
|
||||
if (policyEnforcer != null && reason == Player.DISCONTINUITY_REASON_PERIOD_TRANSITION) {
|
||||
public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition,
|
||||
@NonNull Player.PositionInfo newPosition,
|
||||
int reason)
|
||||
{
|
||||
if (policyEnforcer != null && reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
|
||||
if (policyEnforcer.endPlayback()) {
|
||||
player.stop();
|
||||
}
|
||||
|
||||
@@ -108,13 +108,13 @@ public final class GiphyMp4ProjectionRecycler implements GiphyMp4PlaybackControl
|
||||
}
|
||||
|
||||
private void startPlayback(@NonNull GiphyMp4ProjectionPlayerHolder holder, @NonNull GiphyMp4Playable giphyMp4Playable) {
|
||||
if (!Objects.equals(holder.getMediaSource(), giphyMp4Playable.getMediaSource())) {
|
||||
if (!Objects.equals(holder.getMediaItem(), giphyMp4Playable.getMediaItem())) {
|
||||
holder.setOnPlaybackReady(null);
|
||||
giphyMp4Playable.showProjectionArea();
|
||||
|
||||
holder.show();
|
||||
holder.setOnPlaybackReady(giphyMp4Playable::hideProjectionArea);
|
||||
holder.playContent(giphyMp4Playable.getMediaSource(), giphyMp4Playable.getPlaybackPolicyEnforcer());
|
||||
holder.playContent(giphyMp4Playable.getMediaItem(), giphyMp4Playable.getPlaybackPolicyEnforcer());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.giph.mp4;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
@@ -13,7 +12,7 @@ import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
|
||||
@@ -56,7 +55,8 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override protected void dispatchDraw(Canvas canvas) {
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
if (cornerMask != null) {
|
||||
@@ -69,8 +69,9 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
|
||||
this.exoPlayer = exoPlayer;
|
||||
}
|
||||
|
||||
void setVideoSource(@NonNull MediaSource mediaSource) {
|
||||
exoPlayer.prepare(mediaSource);
|
||||
void setVideoItem(@NonNull MediaItem mediaItem) {
|
||||
exoPlayer.setMediaItem(mediaItem);
|
||||
exoPlayer.prepare();
|
||||
}
|
||||
|
||||
void setCorners(@Nullable Projection.Corners corners) {
|
||||
@@ -91,7 +92,8 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
|
||||
|
||||
void stop() {
|
||||
if (exoPlayer != null) {
|
||||
exoPlayer.stop(true);
|
||||
exoPlayer.stop();
|
||||
exoPlayer.clearMediaItems();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +109,8 @@ public final class GiphyMp4VideoPlayer extends FrameLayout implements DefaultLif
|
||||
exoView.setResizeMode(resizeMode);
|
||||
}
|
||||
|
||||
@Override public void onDestroy(@NonNull LifecycleOwner owner) {
|
||||
@Override
|
||||
public void onDestroy(@NonNull LifecycleOwner owner) {
|
||||
if (exoPlayer != null) {
|
||||
exoPlayer.release();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
@@ -36,28 +36,25 @@ final class GiphyMp4ViewHolder extends RecyclerView.ViewHolder implements GiphyM
|
||||
private final ImageView stillImage;
|
||||
private final GiphyMp4Adapter.Callback listener;
|
||||
private final Drawable placeholder;
|
||||
private final GiphyMp4MediaSourceFactory mediaSourceFactory;
|
||||
|
||||
private float aspectRatio;
|
||||
private MediaSource mediaSource;
|
||||
private float aspectRatio;
|
||||
private MediaItem mediaItem;
|
||||
|
||||
GiphyMp4ViewHolder(@NonNull View itemView,
|
||||
@Nullable GiphyMp4Adapter.Callback listener,
|
||||
@NonNull GiphyMp4MediaSourceFactory mediaSourceFactory)
|
||||
@Nullable GiphyMp4Adapter.Callback listener)
|
||||
{
|
||||
super(itemView);
|
||||
this.container = itemView.findViewById(R.id.container);
|
||||
this.listener = listener;
|
||||
this.stillImage = itemView.findViewById(R.id.still_image);
|
||||
this.placeholder = new ColorDrawable(Util.getRandomElement(ChatColorsPalette.Names.getAll()).getColor(itemView.getContext()));
|
||||
this.mediaSourceFactory = mediaSourceFactory;
|
||||
|
||||
container.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH);
|
||||
}
|
||||
|
||||
void onBind(@NonNull GiphyImage giphyImage) {
|
||||
aspectRatio = giphyImage.getGifAspectRatio();
|
||||
mediaSource = mediaSourceFactory.create(Uri.parse(giphyImage.getMp4PreviewUrl()));
|
||||
mediaItem = MediaItem.fromUri(Uri.parse(giphyImage.getMp4PreviewUrl()));
|
||||
|
||||
container.setAspectRatio(aspectRatio);
|
||||
|
||||
@@ -77,8 +74,8 @@ final class GiphyMp4ViewHolder extends RecyclerView.ViewHolder implements GiphyM
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull MediaSource getMediaSource() {
|
||||
return mediaSource;
|
||||
public @NonNull MediaItem getMediaItem() {
|
||||
return mediaItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user