Create a SignalDataSource class for all of our ExoPlayer needs.

Also fixes an issue around GIF playback within a conversation.
fork-5.53.8
Greyson Parrelli 2021-09-17 09:58:11 -04:00 zatwierdzone przez GitHub
rodzic 6f6da699a3
commit d5fbd10406
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
12 zmienionych plików z 143 dodań i 153 usunięć

Wyświetl plik

@ -5,12 +5,12 @@ import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.DefaultLoadControl import com.google.android.exoplayer2.DefaultLoadControl
import com.google.android.exoplayer2.ForwardingPlayer import com.google.android.exoplayer2.ForwardingPlayer
import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.SimpleExoPlayer
import org.thoughtcrime.securesms.video.exo.AttachmentMediaSourceFactory import org.thoughtcrime.securesms.video.exo.SignalMediaSourceFactory
class VoiceNotePlayer @JvmOverloads constructor( class VoiceNotePlayer @JvmOverloads constructor(
context: Context, context: Context,
val internalPlayer: SimpleExoPlayer = SimpleExoPlayer.Builder(context) val internalPlayer: SimpleExoPlayer = SimpleExoPlayer.Builder(context)
.setMediaSourceFactory(AttachmentMediaSourceFactory(context)) .setMediaSourceFactory(SignalMediaSourceFactory(context))
.setLoadControl( .setLoadControl(
DefaultLoadControl.Builder() DefaultLoadControl.Builder()
.setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE) .setBufferDurationsMs(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE)

Wyświetl plik

@ -389,6 +389,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
int defaultTopMargin = readDimen(R.dimen.message_bubble_default_footer_bottom_margin); int defaultTopMargin = readDimen(R.dimen.message_bubble_default_footer_bottom_margin);
int defaultBottomMargin = readDimen(R.dimen.message_bubble_bottom_padding); int defaultBottomMargin = readDimen(R.dimen.message_bubble_bottom_padding);
int collapsedBottomMargin = readDimen(R.dimen.message_bubble_collapsed_bottom_padding); int collapsedBottomMargin = readDimen(R.dimen.message_bubble_collapsed_bottom_padding);
if (!updatingFooter && if (!updatingFooter &&
getActiveFooter(messageRecord) == footer && getActiveFooter(messageRecord) == footer &&
!hasAudio(messageRecord) && !hasAudio(messageRecord) &&
@ -400,11 +401,13 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
int footerWidth = footer.getMeasuredWidth(); int footerWidth = footer.getMeasuredWidth();
int availableWidth = getAvailableMessageBubbleWidth(bodyText); int availableWidth = getAvailableMessageBubbleWidth(bodyText);
int collapsedTopMargin = -1 * (dateView.getMeasuredHeight() + ViewUtil.dpToPx(4)); int collapsedTopMargin = -1 * (dateView.getMeasuredHeight() + ViewUtil.dpToPx(4));
if (bodyText.isSingleLine()) { if (bodyText.isSingleLine()) {
int maxBubbleWidth = hasBigImageLinkPreview(messageRecord) || hasThumbnail(messageRecord) ? readDimen(R.dimen.media_bubble_max_width) : getMaxBubbleWidth(); int maxBubbleWidth = hasBigImageLinkPreview(messageRecord) || hasThumbnail(messageRecord) ? readDimen(R.dimen.media_bubble_max_width) : getMaxBubbleWidth();
int bodyMargins = ViewUtil.getLeftMargin(bodyText) + ViewUtil.getRightMargin(bodyText); int bodyMargins = ViewUtil.getLeftMargin(bodyText) + ViewUtil.getRightMargin(bodyText);
int sizeWithMargins = bodyText.getMeasuredWidth() + ViewUtil.dpToPx(6) + footerWidth + bodyMargins; int sizeWithMargins = bodyText.getMeasuredWidth() + ViewUtil.dpToPx(6) + footerWidth + bodyMargins;
int minSize = Math.min(maxBubbleWidth, Math.max(bodyText.getMeasuredWidth() + ViewUtil.dpToPx(6) + footerWidth + bodyMargins, bodyBubble.getMeasuredWidth())); int minSize = Math.min(maxBubbleWidth, Math.max(bodyText.getMeasuredWidth() + ViewUtil.dpToPx(6) + footerWidth + bodyMargins, bodyBubble.getMeasuredWidth()));
if (hasQuote(messageRecord) && sizeWithMargins < availableWidth) { if (hasQuote(messageRecord) && sizeWithMargins < availableWidth) {
ViewUtil.setTopMargin(footer, collapsedTopMargin); ViewUtil.setTopMargin(footer, collapsedTopMargin);
ViewUtil.setBottomMargin(footer, collapsedBottomMargin); ViewUtil.setBottomMargin(footer, collapsedBottomMargin);
@ -418,6 +421,7 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
updatingFooter = true; updatingFooter = true;
} }
} }
if (!updatingFooter && bodyText.getLastLineWidth() + ViewUtil.dpToPx(6) + footerWidth <= bodyText.getMeasuredWidth()) { if (!updatingFooter && bodyText.getLastLineWidth() + ViewUtil.dpToPx(6) + footerWidth <= bodyText.getMeasuredWidth()) {
ViewUtil.setTopMargin(footer, collapsedTopMargin); ViewUtil.setTopMargin(footer, collapsedTopMargin);
ViewUtil.setBottomMargin(footer, collapsedBottomMargin); ViewUtil.setBottomMargin(footer, collapsedBottomMargin);
@ -1044,9 +1048,9 @@ public final class ConversationItem extends RelativeLayout implements BindableCo
mediaThumbnailStub.require().setMinimumThumbnailWidth(readDimen(isCaptionlessMms(messageRecord) ? R.dimen.media_bubble_min_width_solo mediaThumbnailStub.require().setMinimumThumbnailWidth(readDimen(isCaptionlessMms(messageRecord) ? R.dimen.media_bubble_min_width_solo
: R.dimen.media_bubble_min_width_with_content)); : R.dimen.media_bubble_min_width_with_content));
mediaThumbnailStub.require().setImageResource(glideRequests, mediaThumbnailStub.require().setImageResource(glideRequests,
thumbnailSlides, thumbnailSlides,
showControls, showControls,
false); false);
mediaThumbnailStub.require().setThumbnailClickListener(new ThumbnailClickListener()); mediaThumbnailStub.require().setThumbnailClickListener(new ThumbnailClickListener());
mediaThumbnailStub.require().setDownloadClickListener(downloadClickListener); mediaThumbnailStub.require().setDownloadClickListener(downloadClickListener);
mediaThumbnailStub.require().setOnLongClickListener(passthroughClickListener); mediaThumbnailStub.require().setOnLongClickListener(passthroughClickListener);

Wyświetl plik

@ -15,7 +15,7 @@ import com.google.android.exoplayer2.upstream.DataSource;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.net.ContentProxySelector; import org.thoughtcrime.securesms.net.ContentProxySelector;
import org.thoughtcrime.securesms.video.exo.ChunkedDataSourceFactory; import org.thoughtcrime.securesms.video.exo.SignalDataSource;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -26,7 +26,7 @@ final class GiphyMp4ExoPlayerProvider implements DefaultLifecycleObserver {
private final Context context; private final Context context;
private final OkHttpClient okHttpClient = ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(new ContentProxySelector()).build(); private final OkHttpClient okHttpClient = ApplicationDependencies.getOkHttpClient().newBuilder().proxySelector(new ContentProxySelector()).build();
private final DataSource.Factory dataSourceFactory = new ChunkedDataSourceFactory(okHttpClient, null); private final DataSource.Factory dataSourceFactory = new SignalDataSource.Factory(ApplicationDependencies.getApplication(), okHttpClient, null);
private final MediaSourceFactory mediaSourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory); private final MediaSourceFactory mediaSourceFactory = new ProgressiveMediaSource.Factory(dataSourceFactory);
GiphyMp4ExoPlayerProvider(@NonNull Context context) { GiphyMp4ExoPlayerProvider(@NonNull Context context) {

Wyświetl plik

@ -27,7 +27,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
@ -36,12 +35,13 @@ import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.ui.PlayerControlView;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import org.signal.core.util.logging.Log; import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.mms.VideoSlide; import org.thoughtcrime.securesms.mms.VideoSlide;
import org.thoughtcrime.securesms.video.exo.AttachmentDataSourceFactory; import org.thoughtcrime.securesms.video.exo.SignalDataSource;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -86,9 +86,8 @@ public class VideoPlayer extends FrameLayout {
Context context = getContext(); Context context = getContext();
if (exoPlayer == null) { if (exoPlayer == null) {
DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, "GenericUserAgent", null); DataSource.Factory attachmentDataSourceFactory = new SignalDataSource.Factory(context, null, null);
AttachmentDataSourceFactory attachmentDataSourceFactory = new AttachmentDataSourceFactory(context, defaultDataSourceFactory, null); MediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(attachmentDataSourceFactory);
MediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(attachmentDataSourceFactory);
exoPlayer = new SimpleExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build(); exoPlayer = new SimpleExoPlayer.Builder(context).setMediaSourceFactory(mediaSourceFactory).build();
exoPlayer.addListener(new ExoPlayerListener(this, window, playerStateCallback, playerPositionDiscontinuityCallback)); exoPlayer.addListener(new ExoPlayerListener(this, window, playerStateCallback, playerPositionDiscontinuityCallback));

Wyświetl plik

@ -1,70 +0,0 @@
package org.thoughtcrime.securesms.video.exo;
import android.net.Uri;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AttachmentDataSource implements DataSource {
private final DefaultDataSource defaultDataSource;
private final PartDataSource partDataSource;
private final BlobDataSource blobDataSource;
private DataSource dataSource;
public AttachmentDataSource(DefaultDataSource defaultDataSource,
PartDataSource partDataSource,
BlobDataSource blobDataSource)
{
this.defaultDataSource = defaultDataSource;
this.partDataSource = partDataSource;
this.blobDataSource = blobDataSource;
}
@Override
public void addTransferListener(@NonNull TransferListener transferListener) {
}
@Override
public long open(DataSpec dataSpec) throws IOException {
if (BlobProvider.isAuthority(dataSpec.uri)) dataSource = blobDataSource;
else if (PartAuthority.isLocalUri(dataSpec.uri)) dataSource = partDataSource;
else dataSource = defaultDataSource;
return dataSource.open(dataSpec);
}
@Override
public int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException {
return dataSource.read(buffer, offset, readLength);
}
@Override
public Uri getUri() {
return dataSource.getUri();
}
@Override
public @NonNull Map<String, List<String>> getResponseHeaders() {
return Collections.emptyMap();
}
@Override
public void close() throws IOException {
dataSource.close();
}
}

Wyświetl plik

@ -1,35 +0,0 @@
package org.thoughtcrime.securesms.video.exo;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.TransferListener;
public class AttachmentDataSourceFactory implements DataSource.Factory {
private final Context context;
private final DefaultDataSourceFactory defaultDataSourceFactory;
private final TransferListener listener;
public AttachmentDataSourceFactory(@NonNull Context context,
@NonNull DefaultDataSourceFactory defaultDataSourceFactory,
@Nullable TransferListener listener)
{
this.context = context;
this.defaultDataSourceFactory = defaultDataSourceFactory;
this.listener = listener;
}
@Override
public @NonNull AttachmentDataSource createDataSource() {
return new AttachmentDataSource(defaultDataSourceFactory.createDataSource(),
new PartDataSource(context, listener),
new BlobDataSource(context, listener));
}
}

Wyświetl plik

@ -20,7 +20,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class BlobDataSource implements DataSource { class BlobDataSource implements DataSource {
private final @NonNull Context context; private final @NonNull Context context;
private final @Nullable TransferListener listener; private final @Nullable TransferListener listener;

Wyświetl plik

@ -27,7 +27,7 @@ import okhttp3.OkHttpClient;
/** /**
* DataSource which utilizes ChunkedDataFetcher to download video content via Signal content proxy. * DataSource which utilizes ChunkedDataFetcher to download video content via Signal content proxy.
*/ */
public class ChunkedDataSource implements DataSource { class ChunkedDataSource implements DataSource {
private final OkHttpClient okHttpClient; private final OkHttpClient okHttpClient;
private final TransferListener transferListener; private final TransferListener transferListener;
@ -138,5 +138,4 @@ public class ChunkedDataSource implements DataSource {
} }
cacheEntry = null; cacheEntry = null;
} }
} }

Wyświetl plik

@ -1,26 +0,0 @@
package org.thoughtcrime.securesms.video.exo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
import okhttp3.OkHttpClient;
public class ChunkedDataSourceFactory implements DataSource.Factory {
private final OkHttpClient okHttpClient;
private final TransferListener listener;
public ChunkedDataSourceFactory(@NonNull OkHttpClient okHttpClient, @Nullable TransferListener listener) {
this.okHttpClient = okHttpClient;
this.listener = listener;
}
@Override
public @NonNull DataSource createDataSource() {
return new ChunkedDataSource(okHttpClient, listener);
}
}

Wyświetl plik

@ -23,7 +23,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class PartDataSource implements DataSource { class PartDataSource implements DataSource {
private final @NonNull Context context; private final @NonNull Context context;
private final @Nullable TransferListener listener; private final @Nullable TransferListener listener;

Wyświetl plik

@ -0,0 +1,119 @@
package org.thoughtcrime.securesms.video.exo;
import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.TransferListener;
import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.providers.BlobProvider;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import okhttp3.OkHttpClient;
/**
* Go-to {@link DataSource} that handles all of our various types of video sources.
* Will defer to other {@link DataSource}s depending on the URI.
*/
public class SignalDataSource implements DataSource {
private final DefaultDataSource defaultDataSource;
private final PartDataSource partDataSource;
private final BlobDataSource blobDataSource;
private final ChunkedDataSource chunkedDataSource;
private DataSource dataSource;
public SignalDataSource(@NonNull DefaultDataSource defaultDataSource,
@NonNull PartDataSource partDataSource,
@NonNull BlobDataSource blobDataSource,
@Nullable ChunkedDataSource chunkedDataSource)
{
this.defaultDataSource = defaultDataSource;
this.partDataSource = partDataSource;
this.blobDataSource = blobDataSource;
this.chunkedDataSource = chunkedDataSource;
}
@Override
public void addTransferListener(@NonNull TransferListener transferListener) {
}
@Override
public long open(DataSpec dataSpec) throws IOException {
if (BlobProvider.isAuthority(dataSpec.uri)) {
dataSource = blobDataSource;
} else if (PartAuthority.isLocalUri(dataSpec.uri)) {
dataSource = partDataSource;
} else if (chunkedDataSource != null && isRemoteUri(dataSpec.uri)) {
dataSource = chunkedDataSource;
} else {
dataSource = defaultDataSource;
}
return dataSource.open(dataSpec);
}
@Override
public int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException {
return dataSource.read(buffer, offset, readLength);
}
@Override
public @Nullable Uri getUri() {
return dataSource.getUri();
}
@Override
public @NonNull Map<String, List<String>> getResponseHeaders() {
return Collections.emptyMap();
}
@Override
public void close() throws IOException {
dataSource.close();
}
private static boolean isRemoteUri(@Nullable Uri uri) {
if (uri != null) {
String scheme = uri.getScheme();
return "http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme);
} else {
return false;
}
}
public static final class Factory implements DataSource.Factory {
private final Context context;
private final OkHttpClient okHttpClient;
private final TransferListener listener;
public Factory(@NonNull Context context,
@Nullable OkHttpClient okHttpClient,
@Nullable TransferListener listener)
{
this.context = context;
this.okHttpClient = okHttpClient;
this.listener = listener;
}
@Override
public @NonNull SignalDataSource createDataSource() {
return new SignalDataSource(new DefaultDataSourceFactory(context, "GenericUserAgent", null).createDataSource(),
new PartDataSource(context, listener),
new BlobDataSource(context, listener),
okHttpClient != null ? new ChunkedDataSource(okHttpClient, listener) : null);
}
}
}

Wyświetl plik

@ -17,6 +17,7 @@ import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
@ -24,17 +25,16 @@ import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import java.util.List; import java.util.List;
/** /**
* This class is responsible for creating a MediaSource object for a given Uri, using AttachmentDataSourceFactory * This class is responsible for creating a MediaSource object for a given Uri, using {@link SignalDataSource.Factory}.
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public final class AttachmentMediaSourceFactory implements MediaSourceFactory { public final class SignalMediaSourceFactory implements MediaSourceFactory {
private final ProgressiveMediaSource.Factory progressiveMediaSourceFactory; private final ProgressiveMediaSource.Factory progressiveMediaSourceFactory;
public AttachmentMediaSourceFactory(@NonNull Context context) { public SignalMediaSourceFactory(@NonNull Context context) {
DefaultDataSourceFactory defaultDataSourceFactory = new DefaultDataSourceFactory(context, "GenericUserAgent", null); DataSource.Factory attachmentDataSourceFactory = new SignalDataSource.Factory(context, null, null);
AttachmentDataSourceFactory attachmentDataSourceFactory = new AttachmentDataSourceFactory(context, defaultDataSourceFactory, null); ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
progressiveMediaSourceFactory = new ProgressiveMediaSource.Factory(attachmentDataSourceFactory, extractorsFactory); progressiveMediaSourceFactory = new ProgressiveMediaSource.Factory(attachmentDataSourceFactory, extractorsFactory);
} }