mirror of
https://codeberg.org/Bazsalanszky/Infinity-For-Lemmy.git
synced 2025-01-14 20:27:12 +01:00
Update ExoPlayer.
This commit is contained in:
parent
84b5b1d95d
commit
7293b9e758
@ -72,17 +72,12 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.5.0-alpha05'
|
||||
|
||||
/** ExoPlayer **/
|
||||
def exoplayerVersion = "2.10.8"
|
||||
def exoplayerVersion = "2.18.1"
|
||||
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayerVersion"
|
||||
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayerVersion"
|
||||
implementation "com.google.android.exoplayer:exoplayer-hls:$exoplayerVersion"
|
||||
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayerVersion"
|
||||
implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$exoplayerVersion"
|
||||
/*def toroVersion = "3.7.0.2010003"
|
||||
implementation "im.ene.toro3:toro:$toroVersion"
|
||||
implementation("im.ene.toro3:toro-ext-exoplayer:$toroVersion") {
|
||||
exclude module: 'extension-ima'
|
||||
}*/
|
||||
|
||||
/** Third-party **/
|
||||
|
||||
|
@ -27,9 +27,9 @@ import androidx.core.content.ContextCompat;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.RequestManager;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
@ -189,7 +189,7 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF
|
||||
private FlairBottomSheetFragment mFlairSelectionBottomSheetFragment;
|
||||
private Snackbar mPostingSnackbar;
|
||||
private DataSource.Factory dataSourceFactory;
|
||||
private SimpleExoPlayer player;
|
||||
private ExoPlayer player;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -216,7 +216,7 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF
|
||||
|
||||
mGlide = Glide.with(this);
|
||||
|
||||
player = ExoPlayerFactory.newSimpleInstance(this);
|
||||
player = new ExoPlayer.Builder(this).build();
|
||||
videoPlayerView.setPlayer(player);
|
||||
dataSourceFactory = new DefaultDataSourceFactory(this,
|
||||
Util.getUserAgent(this, "Infinity"));
|
||||
@ -491,7 +491,7 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF
|
||||
constraintLayout.setVisibility(View.GONE);
|
||||
selectAgainTextView.setVisibility(View.VISIBLE);
|
||||
videoPlayerView.setVisibility(View.VISIBLE);
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(videoUri));
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(videoUri)));
|
||||
player.setPlayWhenReady(true);
|
||||
wasPlaying = true;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@ -24,6 +25,7 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.text.Html;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.OrientationEventListener;
|
||||
@ -45,29 +47,26 @@ import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
||||
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.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.video.VideoListener;
|
||||
import com.google.android.exoplayer2.video.VideoSize;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.otaliastudios.zoom.ZoomEngine;
|
||||
import com.otaliastudios.zoom.ZoomSurfaceView;
|
||||
|
||||
@ -176,7 +175,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
public Typeface typeface;
|
||||
|
||||
private Uri mVideoUri;
|
||||
private SimpleExoPlayer player;
|
||||
private ExoPlayer player;
|
||||
private DefaultTrackSelector trackSelector;
|
||||
private DataSource.Factory dataSourceFactory;
|
||||
|
||||
@ -403,7 +402,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
String postTitle = intent.getStringExtra(EXTRA_POST_TITLE);
|
||||
setSmallTitle(postTitle);
|
||||
|
||||
playerControlView.setVisibilityListener(visibility -> {
|
||||
playerControlView.addVisibilityListener(visibility -> {
|
||||
switch (visibility) {
|
||||
case View.GONE:
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
@ -422,21 +421,20 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
}
|
||||
});
|
||||
|
||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
trackSelector = new DefaultTrackSelector(this);
|
||||
if (videoType == VIDEO_TYPE_NORMAL && isDataSavingMode && dataSavingModeDefaultResolution > 0) {
|
||||
trackSelector.setParameters(
|
||||
trackSelector.buildUponParameters()
|
||||
.setMaxVideoSize(dataSavingModeDefaultResolution, dataSavingModeDefaultResolution));
|
||||
}
|
||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
|
||||
player = new ExoPlayer.Builder(this).setTrackSelector(trackSelector).build();
|
||||
|
||||
playerControlView.setPlayer(player);
|
||||
|
||||
player.addVideoListener(new VideoListener() {
|
||||
player.addListener(new Player.Listener() {
|
||||
@Override
|
||||
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
|
||||
zoomSurfaceView.setContentSize(width, height);
|
||||
public void onVideoSizeChanged(VideoSize videoSize) {
|
||||
zoomSurfaceView.setContentSize(videoSize.width, videoSize.height);
|
||||
}
|
||||
});
|
||||
zoomSurfaceView.addCallback(new ZoomSurfaceView.Callback() {
|
||||
@ -506,9 +504,10 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
if (mVideoUri == null) {
|
||||
loadStreamableVideo(shortCode, savedInstanceState);
|
||||
} else {
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
preparePlayer(savedInstanceState);
|
||||
}
|
||||
} else if (videoType == VIDEO_TYPE_V_REDD_IT) {
|
||||
@ -537,9 +536,10 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
loadGfycatOrRedgifsVideo(redgifsRetrofit, gfycatId, false, savedInstanceState, false);
|
||||
}
|
||||
} else {
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
preparePlayer(savedInstanceState);
|
||||
}
|
||||
} else if (videoType == VIDEO_TYPE_DIRECT || videoType == VIDEO_TYPE_IMGUR) {
|
||||
@ -550,10 +550,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl);
|
||||
}
|
||||
// Produces DataSource instances through which media data is loaded.
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
// Prepare the player with the source.
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
preparePlayer(savedInstanceState);
|
||||
} else {
|
||||
videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL);
|
||||
@ -561,10 +562,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
id = intent.getStringExtra(EXTRA_ID);
|
||||
videoFileName = subredditName + "-" + id + ".mp4";
|
||||
// Produces DataSource instances through which media data is loaded.
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
// Prepare the player with the source.
|
||||
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
preparePlayer(savedInstanceState);
|
||||
}
|
||||
}
|
||||
@ -615,25 +617,29 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
|
||||
}
|
||||
|
||||
player.addListener(new Player.EventListener() {
|
||||
player.addListener(new Player.Listener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
if (videoType == VIDEO_TYPE_NORMAL) {
|
||||
hdButton.setVisibility(View.VISIBLE);
|
||||
hdButton.setOnClickListener(view -> {
|
||||
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(ViewVideoActivity.this, getString(R.string.select_video_quality), trackSelector, 0);
|
||||
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(ViewVideoActivity.this, getString(R.string.select_video_quality), player, 0);
|
||||
builder.setShowDisableOption(true);
|
||||
builder.setAllowAdaptiveSelections(false);
|
||||
AlertDialog alertDialog = builder.build();
|
||||
alertDialog.show();
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
Dialog dialog = builder.build();
|
||||
dialog.show();
|
||||
if (dialog instanceof AlertDialog) {
|
||||
Log.i("asfadsf", "asdfasdf");
|
||||
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
muteButton.setVisibility(View.VISIBLE);
|
||||
muteButton.setOnClickListener(view -> {
|
||||
@ -688,10 +694,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
progressBar.setVisibility(View.GONE);
|
||||
mVideoUri = Uri.parse(webm);
|
||||
videoDownloadUrl = mp4;
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
preparePlayer(savedInstanceState);
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -717,10 +724,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
progressBar.setVisibility(View.GONE);
|
||||
mVideoUri = Uri.parse(webm);
|
||||
videoDownloadUrl = mp4;
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
preparePlayer(savedInstanceState);
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -782,10 +790,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
videoType = VIDEO_TYPE_IMGUR;
|
||||
videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl);
|
||||
// Produces DataSource instances through which media data is loaded.
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
// Prepare the player with the source.
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
preparePlayer(savedInstanceState);
|
||||
} else {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
@ -797,11 +806,12 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
|
||||
videoFileName = subredditName + "-" + id + ".mp4";
|
||||
// Produces DataSource instances through which media data is loaded.
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
// Prepare the player with the source.
|
||||
preparePlayer(savedInstanceState);
|
||||
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
} else {
|
||||
Toast.makeText(ViewVideoActivity.this, R.string.error_fetching_v_redd_it_video_cannot_get_video_url, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
@ -839,10 +849,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
|
||||
progressBar.setVisibility(View.GONE);
|
||||
videoDownloadUrl = streamableVideo.mp4 == null ? streamableVideo.mp4Mobile.url : streamableVideo.mp4.url;
|
||||
mVideoUri = Uri.parse(videoDownloadUrl);
|
||||
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache,
|
||||
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
|
||||
preparePlayer(savedInstanceState);
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,10 +42,12 @@ import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.libRG.CustomTextView;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
@ -2755,10 +2757,11 @@ public class HistoryPostRecyclerViewAdapter extends PagingDataAdapter<Post, Recy
|
||||
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
|
||||
helper.addEventListener(new Playable.DefaultEventListener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
if (mFragment.getMasterMutingOption() != null) {
|
||||
volume = mFragment.getMasterMutingOption() ? 0f : 1f;
|
||||
@ -4022,10 +4025,11 @@ public class HistoryPostRecyclerViewAdapter extends PagingDataAdapter<Post, Recy
|
||||
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
|
||||
helper.addEventListener(new Playable.DefaultEventListener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
if (mFragment.getMasterMutingOption() != null) {
|
||||
volume = mFragment.getMasterMutingOption() ? 0f : 1f;
|
||||
|
@ -40,10 +40,10 @@ import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.libRG.CustomTextView;
|
||||
|
||||
import org.commonmark.ext.gfm.tables.TableBlock;
|
||||
@ -1761,10 +1761,11 @@ public class PostDetailRecyclerViewAdapter extends RecyclerView.Adapter<Recycler
|
||||
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
|
||||
helper.addEventListener(new Playable.DefaultEventListener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
helper.setVolume(volume);
|
||||
muteButton.setVisibility(View.VISIBLE);
|
||||
|
@ -42,10 +42,12 @@ import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.libRG.CustomTextView;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
@ -2860,10 +2862,11 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
|
||||
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
|
||||
helper.addEventListener(new Playable.DefaultEventListener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
if (mFragment.getMasterMutingOption() != null) {
|
||||
volume = mFragment.getMasterMutingOption() ? 0f : 1f;
|
||||
@ -4175,10 +4178,11 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
|
||||
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
|
||||
helper.addEventListener(new Playable.DefaultEventListener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
if (mFragment.getMasterMutingOption() != null) {
|
||||
volume = mFragment.getMasterMutingOption() ? 0f : 1f;
|
||||
|
@ -4,8 +4,8 @@ import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.Config;
|
||||
@ -22,8 +22,8 @@ public class LoopAvailableExoCreator extends DefaultExoCreator {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SimpleExoPlayer createPlayer() {
|
||||
SimpleExoPlayer player = super.createPlayer();
|
||||
public ExoPlayer createPlayer() {
|
||||
ExoPlayer player = super.createPlayer();
|
||||
if (sharedPreferences.getBoolean(SharedPreferencesUtils.LOOP_VIDEO, true)) {
|
||||
player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
||||
} else {
|
||||
|
@ -7,7 +7,6 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@ -26,22 +25,21 @@ import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
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.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
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.util.Util;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@ -54,6 +52,7 @@ import ml.docilealligator.infinityforreddit.R;
|
||||
import ml.docilealligator.infinityforreddit.activities.ViewImgurMediaActivity;
|
||||
import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment;
|
||||
import ml.docilealligator.infinityforreddit.services.DownloadMediaService;
|
||||
import ml.docilealligator.infinityforreddit.utils.APIUtils;
|
||||
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
|
||||
import ml.docilealligator.infinityforreddit.utils.Utils;
|
||||
|
||||
@ -78,7 +77,7 @@ public class ViewImgurVideoFragment extends Fragment {
|
||||
ImageView downloadImageView;
|
||||
private ViewImgurMediaActivity activity;
|
||||
private ImgurMedia imgurMedia;
|
||||
private SimpleExoPlayer player;
|
||||
private ExoPlayer player;
|
||||
private DataSource.Factory dataSourceFactory;
|
||||
private boolean wasPlaying = false;
|
||||
private boolean isMute = false;
|
||||
@ -87,6 +86,8 @@ public class ViewImgurVideoFragment extends Fragment {
|
||||
@Inject
|
||||
@Named("default")
|
||||
SharedPreferences mSharedPreferences;
|
||||
@Inject
|
||||
SimpleCache mSimpleCache;
|
||||
|
||||
public ViewImgurVideoFragment() {
|
||||
// Required empty public constructor
|
||||
@ -143,13 +144,13 @@ public class ViewImgurVideoFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector);
|
||||
TrackSelector trackSelector = new DefaultTrackSelector(activity);
|
||||
player = new ExoPlayer.Builder(activity).setTrackSelector(trackSelector).build();
|
||||
videoPlayerView.setPlayer(player);
|
||||
dataSourceFactory = new DefaultDataSourceFactory(activity,
|
||||
Util.getUserAgent(activity, "Infinity"));
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(imgurMedia.getLink())));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(activity)));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(imgurMedia.getLink())));
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
playbackSpeed = savedInstanceState.getInt(PLAYBACK_SPEED_STATE);
|
||||
@ -278,12 +279,13 @@ public class ViewImgurVideoFragment extends Fragment {
|
||||
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
|
||||
}
|
||||
|
||||
player.addListener(new Player.EventListener() {
|
||||
player.addListener(new Player.Listener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
muteButton.setVisibility(View.VISIBLE);
|
||||
muteButton.setOnClickListener(view -> {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package ml.docilealligator.infinityforreddit.fragments;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
@ -23,21 +23,20 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
|
||||
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.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@ -58,6 +57,7 @@ import ml.docilealligator.infinityforreddit.activities.RPANActivity;
|
||||
import ml.docilealligator.infinityforreddit.adapters.RPANCommentStreamRecyclerViewAdapter;
|
||||
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
|
||||
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
|
||||
import ml.docilealligator.infinityforreddit.utils.APIUtils;
|
||||
import ml.docilealligator.infinityforreddit.utils.JSONUtils;
|
||||
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
|
||||
import okhttp3.OkHttpClient;
|
||||
@ -105,9 +105,11 @@ public class ViewRPANBroadcastFragment extends Fragment {
|
||||
@Inject
|
||||
@Named("rpan")
|
||||
OkHttpClient okHttpClient;
|
||||
@Inject
|
||||
SimpleCache mSimpleCache;
|
||||
private RPANActivity mActivity;
|
||||
private RPANBroadcast rpanBroadcast;
|
||||
private SimpleExoPlayer player;
|
||||
private ExoPlayer player;
|
||||
private DefaultTrackSelector trackSelector;
|
||||
private DataSource.Factory dataSourceFactory;
|
||||
private Handler handler;
|
||||
@ -175,9 +177,8 @@ public class ViewRPANBroadcastFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
player = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector);
|
||||
trackSelector = new DefaultTrackSelector(mActivity);
|
||||
player = new ExoPlayer.Builder(mActivity).setTrackSelector(trackSelector).build();
|
||||
playerView.setPlayer(player);
|
||||
|
||||
wasPlaying = true;
|
||||
@ -202,9 +203,10 @@ public class ViewRPANBroadcastFragment extends Fragment {
|
||||
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
|
||||
}
|
||||
|
||||
player.addListener(new Player.EventListener() {
|
||||
player.addListener(new Player.Listener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
if (isDataSavingMode) {
|
||||
trackSelector.setParameters(
|
||||
@ -214,18 +216,19 @@ public class ViewRPANBroadcastFragment extends Fragment {
|
||||
|
||||
hdButton.setVisibility(View.VISIBLE);
|
||||
hdButton.setOnClickListener(view -> {
|
||||
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(mActivity,
|
||||
getString(R.string.select_video_quality), trackSelector, 0);
|
||||
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(mActivity, getString(R.string.select_video_quality), player, 0);
|
||||
builder.setShowDisableOption(true);
|
||||
builder.setAllowAdaptiveSelections(false);
|
||||
AlertDialog alertDialog = builder.build();
|
||||
alertDialog.show();
|
||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
Dialog dialog = builder.build();
|
||||
dialog.show();
|
||||
if (dialog instanceof AlertDialog) {
|
||||
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
|
||||
}
|
||||
});
|
||||
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
muteButton.setVisibility(View.VISIBLE);
|
||||
muteButton.setOnClickListener(view -> {
|
||||
@ -350,9 +353,11 @@ public class ViewRPANBroadcastFragment extends Fragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (dataSourceFactory == null) {
|
||||
dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(mActivity, "Infinity"));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(mActivity)));
|
||||
// Prepare the player with the source.
|
||||
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(rpanBroadcast.rpanStream.hlsUrl)));
|
||||
player.prepare();
|
||||
player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(rpanBroadcast.rpanStream.hlsUrl)));
|
||||
if (mSharedPreferences.getBoolean(SharedPreferencesUtils.LOOP_VIDEO, true)) {
|
||||
player.setRepeatMode(Player.REPEAT_MODE_ALL);
|
||||
} else {
|
||||
|
@ -7,7 +7,6 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@ -26,22 +25,21 @@ import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
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.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
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.util.Util;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
|
||||
import com.google.android.material.bottomappbar.BottomAppBar;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@ -54,6 +52,7 @@ import ml.docilealligator.infinityforreddit.activities.ViewRedditGalleryActivity
|
||||
import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment;
|
||||
import ml.docilealligator.infinityforreddit.post.Post;
|
||||
import ml.docilealligator.infinityforreddit.services.DownloadMediaService;
|
||||
import ml.docilealligator.infinityforreddit.utils.APIUtils;
|
||||
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
|
||||
import ml.docilealligator.infinityforreddit.utils.Utils;
|
||||
|
||||
@ -82,7 +81,7 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
|
||||
private Post.Gallery galleryVideo;
|
||||
private String subredditName;
|
||||
private boolean isNsfw;
|
||||
private SimpleExoPlayer player;
|
||||
private ExoPlayer player;
|
||||
private DataSource.Factory dataSourceFactory;
|
||||
private boolean wasPlaying = false;
|
||||
private boolean isMute = false;
|
||||
@ -91,6 +90,8 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
|
||||
@Inject
|
||||
@Named("default")
|
||||
SharedPreferences mSharedPreferences;
|
||||
@Inject
|
||||
SimpleCache mSimpleCache;
|
||||
|
||||
public ViewRedditGalleryVideoFragment() {
|
||||
// Required empty public constructor
|
||||
@ -153,13 +154,13 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory();
|
||||
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||
player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector);
|
||||
TrackSelector trackSelector = new DefaultTrackSelector(activity);
|
||||
player = new ExoPlayer.Builder(activity).setTrackSelector(trackSelector).build();
|
||||
videoPlayerView.setPlayer(player);
|
||||
dataSourceFactory = new DefaultDataSourceFactory(activity,
|
||||
Util.getUserAgent(activity, "Infinity"));
|
||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(galleryVideo.url)));
|
||||
dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
|
||||
.setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(activity)));
|
||||
player.prepare();
|
||||
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(galleryVideo.url)));
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
playbackSpeed = savedInstanceState.getInt(PLAYBACK_SPEED_STATE);
|
||||
@ -290,12 +291,13 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
|
||||
muteButton.setImageResource(R.drawable.ic_unmute_24dp);
|
||||
}
|
||||
|
||||
player.addListener(new Player.EventListener() {
|
||||
player.addListener(new Player.Listener() {
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (!trackGroups.isEmpty()) {
|
||||
for (int i = 0; i < trackGroups.length; i++) {
|
||||
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType;
|
||||
for (int i = 0; i < trackGroups.size(); i++) {
|
||||
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
|
||||
if (mimeType != null && mimeType.contains("audio")) {
|
||||
muteButton.setVisibility(View.VISIBLE);
|
||||
muteButton.setOnClickListener(view -> {
|
||||
|
@ -29,15 +29,11 @@ import com.google.android.exoplayer2.DefaultLoadControl;
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory.ExtensionRendererMode;
|
||||
import com.google.android.exoplayer2.LoadControl;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.annotations.Beta;
|
||||
|
||||
/**
|
||||
* Necessary configuration for {@link ExoCreator} to produces {@link SimpleExoPlayer} and
|
||||
* {@link MediaSource}. Instance of this class must be construct using {@link Builder}.
|
||||
@ -61,7 +57,6 @@ public final class Config {
|
||||
@NonNull final MediaSourceBuilder mediaSourceBuilder;
|
||||
|
||||
// Nullable options
|
||||
@Nullable final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
|
||||
@Nullable final Cache cache; // null by default
|
||||
// If null, ExoCreator must come up with a default one.
|
||||
// This is to help customizing the Data source, for example using OkHttp extension.
|
||||
@ -72,14 +67,13 @@ public final class Config {
|
||||
@NonNull LoadControl loadControl,
|
||||
@Nullable DataSource.Factory dataSourceFactory,
|
||||
@NonNull MediaSourceBuilder mediaSourceBuilder,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable Cache cache) {
|
||||
@Nullable Cache cache) {
|
||||
this.context = context != null ? context.getApplicationContext() : null;
|
||||
this.extensionMode = extensionMode;
|
||||
this.meter = meter;
|
||||
this.loadControl = loadControl;
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.mediaSourceBuilder = mediaSourceBuilder;
|
||||
this.drmSessionManager = drmSessionManager;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@ -93,7 +87,6 @@ public final class Config {
|
||||
if (!meter.equals(config.meter)) return false;
|
||||
if (!loadControl.equals(config.loadControl)) return false;
|
||||
if (!mediaSourceBuilder.equals(config.mediaSourceBuilder)) return false;
|
||||
if (!ObjectsCompat.equals(drmSessionManager, config.drmSessionManager)) return false;
|
||||
if (!ObjectsCompat.equals(cache, config.cache)) return false;
|
||||
return ObjectsCompat.equals(dataSourceFactory, config.dataSourceFactory);
|
||||
}
|
||||
@ -103,7 +96,6 @@ public final class Config {
|
||||
result = 31 * result + meter.hashCode();
|
||||
result = 31 * result + loadControl.hashCode();
|
||||
result = 31 * result + mediaSourceBuilder.hashCode();
|
||||
result = 31 * result + (drmSessionManager != null ? drmSessionManager.hashCode() : 0);
|
||||
result = 31 * result + (cache != null ? cache.hashCode() : 0);
|
||||
result = 31 * result + (dataSourceFactory != null ? dataSourceFactory.hashCode() : 0);
|
||||
return result;
|
||||
@ -111,7 +103,6 @@ public final class Config {
|
||||
|
||||
@SuppressWarnings("unused") public Builder newBuilder() {
|
||||
return new Builder(context).setCache(this.cache)
|
||||
.setDrmSessionManager(this.drmSessionManager)
|
||||
.setExtensionMode(this.extensionMode)
|
||||
.setLoadControl(this.loadControl)
|
||||
.setMediaSourceBuilder(this.mediaSourceBuilder)
|
||||
@ -145,7 +136,6 @@ public final class Config {
|
||||
private LoadControl loadControl = new DefaultLoadControl();
|
||||
private DataSource.Factory dataSourceFactory = null;
|
||||
private MediaSourceBuilder mediaSourceBuilder = MediaSourceBuilder.DEFAULT;
|
||||
private DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = null;
|
||||
private Cache cache = null;
|
||||
|
||||
public Builder setExtensionMode(@ExtensionRendererMode int extensionMode) {
|
||||
@ -175,13 +165,6 @@ public final class Config {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Beta //
|
||||
public Builder setDrmSessionManager(
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
|
||||
this.drmSessionManager = drmSessionManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCache(@Nullable Cache cache) {
|
||||
this.cache = cache;
|
||||
return this;
|
||||
@ -189,7 +172,7 @@ public final class Config {
|
||||
|
||||
public Config build() {
|
||||
return new Config(context, extensionMode, meter, loadControl, dataSourceFactory,
|
||||
mediaSourceBuilder, drmSessionManager, cache);
|
||||
mediaSourceBuilder, cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,22 +27,26 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.DefaultRenderersFactory;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.LoadControl;
|
||||
import com.google.android.exoplayer2.RenderersFactory;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.LoadEventInfo;
|
||||
import com.google.android.exoplayer2.source.MediaLoadData;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSource;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
|
||||
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.utils.APIUtils;
|
||||
|
||||
/**
|
||||
* Usage: use this as-it or inheritance.
|
||||
*
|
||||
@ -75,13 +79,13 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
|
||||
|
||||
DataSource.Factory baseFactory = config.dataSourceFactory;
|
||||
if (baseFactory == null) {
|
||||
baseFactory = new DefaultHttpDataSourceFactory(toro.appName, config.meter);
|
||||
baseFactory = new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(getContext()));
|
||||
}
|
||||
DataSource.Factory factory = new DefaultDataSourceFactory(this.toro.context, //
|
||||
config.meter, baseFactory);
|
||||
if (config.cache != null) factory = new CacheDataSourceFactory(config.cache, factory);
|
||||
DataSource.Factory factory = new DefaultDataSource.Factory(this.toro.context, baseFactory);
|
||||
if (config.cache != null)
|
||||
factory = new CacheDataSource.Factory().setCache(config.cache).setUpstreamDataSourceFactory(baseFactory);
|
||||
mediaDataSourceFactory = factory;
|
||||
manifestDataSourceFactory = new DefaultDataSourceFactory(this.toro.context, this.toro.appName);
|
||||
manifestDataSourceFactory = new DefaultDataSource.Factory(this.toro.context);
|
||||
}
|
||||
|
||||
public DefaultExoCreator(Context context, Config config) {
|
||||
@ -89,7 +93,8 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
|
||||
}
|
||||
|
||||
@SuppressWarnings("SimplifiableIfStatement")
|
||||
@Override public boolean equals(Object o) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
@ -104,7 +109,8 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
|
||||
return manifestDataSourceFactory.equals(that.manifestDataSourceFactory);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = toro.hashCode();
|
||||
result = 31 * result + trackSelector.hashCode();
|
||||
result = 31 * result + loadControl.hashCode();
|
||||
@ -119,22 +125,28 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
|
||||
return trackSelector;
|
||||
}
|
||||
|
||||
@Nullable @Override public Context getContext() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return toro.context;
|
||||
}
|
||||
|
||||
@NonNull @Override public SimpleExoPlayer createPlayer() {
|
||||
@NonNull
|
||||
@Override
|
||||
public ExoPlayer createPlayer() {
|
||||
return new ToroExoPlayer(toro.context, renderersFactory, trackSelector, loadControl,
|
||||
new DefaultBandwidthMeter.Builder(toro.context).build(), config.drmSessionManager,
|
||||
Util.getLooper());
|
||||
new DefaultBandwidthMeter.Builder(toro.context).build(), Util.getCurrentOrMainLooper()).getPlayer();
|
||||
}
|
||||
|
||||
@NonNull @Override public MediaSource createMediaSource(@NonNull Uri uri, String fileExt) {
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaSource createMediaSource(@NonNull Uri uri, String fileExt) {
|
||||
return mediaSourceBuilder.buildMediaSource(this.toro.context, uri, fileExt, new Handler(),
|
||||
manifestDataSourceFactory, mediaDataSourceFactory, this);
|
||||
}
|
||||
|
||||
@NonNull @Override //
|
||||
@NonNull
|
||||
@Override //
|
||||
public Playable createPlayable(@NonNull Uri uri, String fileExt) {
|
||||
return new PlayableImpl(this, uri, fileExt);
|
||||
}
|
||||
@ -166,28 +178,15 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
|
||||
// no-ops
|
||||
}
|
||||
|
||||
@Override public void onReadingStarted(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
// no-ops
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpstreamDiscarded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId,
|
||||
MediaLoadData mediaLoadData) {
|
||||
// no-ops
|
||||
}
|
||||
|
||||
@Override public void onDownstreamFormatChanged(int windowIndex,
|
||||
@Override
|
||||
public void onDownstreamFormatChanged(int windowIndex,
|
||||
@Nullable MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
|
||||
// no-ops
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodCreated(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
// no-ops
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaPeriodReleased(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
|
||||
// no-ops
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import android.net.Uri;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
|
||||
@ -52,7 +53,8 @@ public interface ExoCreator {
|
||||
*
|
||||
* @return a new {@link SimpleExoPlayer} instance.
|
||||
*/
|
||||
@NonNull SimpleExoPlayer createPlayer();
|
||||
@NonNull
|
||||
ExoPlayer createPlayer();
|
||||
|
||||
/**
|
||||
* Create a {@link MediaSource} from media {@link Uri}.
|
||||
|
@ -17,7 +17,6 @@
|
||||
package ml.docilealligator.infinityforreddit.videoautoplay;
|
||||
|
||||
import static com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS;
|
||||
|
||||
import static ml.docilealligator.infinityforreddit.videoautoplay.ToroExo.toro;
|
||||
|
||||
import android.net.Uri;
|
||||
@ -28,16 +27,16 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.PlaybackException;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer;
|
||||
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.source.BehindLiveWindowException;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.R;
|
||||
|
||||
@ -52,13 +51,14 @@ import ml.docilealligator.infinityforreddit.R;
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class ExoPlayable extends PlayableImpl {
|
||||
|
||||
@SuppressWarnings("unused") private static final String TAG = "ToroExo:Playable";
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "ToroExo:Playable";
|
||||
|
||||
private EventListener listener;
|
||||
|
||||
// Adapt from ExoPlayer demo.
|
||||
protected boolean inErrorState = false;
|
||||
protected TrackGroupArray lastSeenTrackGroupArray;
|
||||
protected ImmutableList<Tracks.Group> lastSeenTrackGroupArray;
|
||||
|
||||
/**
|
||||
* Construct an instance of {@link ExoPlayable} from an {@link ExoCreator} and {@link Uri}. The
|
||||
@ -73,7 +73,8 @@ public class ExoPlayable extends PlayableImpl {
|
||||
super(creator, uri, fileExt);
|
||||
}
|
||||
|
||||
@Override public void prepare(boolean prepareSource) {
|
||||
@Override
|
||||
public void prepare(boolean prepareSource) {
|
||||
if (listener == null) {
|
||||
listener = new Listener();
|
||||
super.addEventListener(listener);
|
||||
@ -83,7 +84,8 @@ public class ExoPlayable extends PlayableImpl {
|
||||
this.inErrorState = false;
|
||||
}
|
||||
|
||||
@Override public void setPlayerView(@Nullable PlayerView playerView) {
|
||||
@Override
|
||||
public void setPlayerView(@Nullable PlayerView playerView) {
|
||||
// This will also clear these flags
|
||||
if (playerView != this.playerView) {
|
||||
this.lastSeenTrackGroupArray = null;
|
||||
@ -92,13 +94,15 @@ public class ExoPlayable extends PlayableImpl {
|
||||
super.setPlayerView(playerView);
|
||||
}
|
||||
|
||||
@Override public void reset() {
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
this.lastSeenTrackGroupArray = null;
|
||||
this.inErrorState = false;
|
||||
}
|
||||
|
||||
@Override public void release() {
|
||||
@Override
|
||||
public void release() {
|
||||
if (listener != null) {
|
||||
super.removeEventListener(listener);
|
||||
listener = null;
|
||||
@ -120,10 +124,9 @@ public class ExoPlayable extends PlayableImpl {
|
||||
}
|
||||
|
||||
class Listener extends DefaultEventListener {
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
super.onTracksChanged(trackGroups, trackSelections);
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
|
||||
if (trackGroups == lastSeenTrackGroupArray) return;
|
||||
lastSeenTrackGroupArray = trackGroups;
|
||||
if (!(creator instanceof DefaultExoCreator)) return;
|
||||
@ -142,34 +145,8 @@ public class ExoPlayable extends PlayableImpl {
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPlayerError(ExoPlaybackException error) {
|
||||
/// Adapt from ExoPlayer Demo
|
||||
String errorString = null;
|
||||
if (error.type == ExoPlaybackException.TYPE_RENDERER) {
|
||||
Exception cause = error.getRendererException();
|
||||
if (cause instanceof MediaCodecRenderer.DecoderInitializationException) {
|
||||
// Special case for decoder initialization failures.
|
||||
MediaCodecRenderer.DecoderInitializationException decoderInitializationException =
|
||||
(MediaCodecRenderer.DecoderInitializationException) cause;
|
||||
if (decoderInitializationException.decoderName == null) {
|
||||
if (decoderInitializationException.getCause() instanceof MediaCodecUtil.DecoderQueryException) {
|
||||
errorString = toro.getString(R.string.error_querying_decoders);
|
||||
} else if (decoderInitializationException.secureDecoderRequired) {
|
||||
errorString = toro.getString(R.string.error_no_secure_decoder,
|
||||
decoderInitializationException.mimeType);
|
||||
} else {
|
||||
errorString = toro.getString(R.string.error_no_decoder,
|
||||
decoderInitializationException.mimeType);
|
||||
}
|
||||
} else {
|
||||
errorString = toro.getString(R.string.error_instantiating_decoder,
|
||||
decoderInitializationException.decoderName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorString != null) onErrorMessage(errorString);
|
||||
|
||||
@Override
|
||||
public void onPlayerError(@NonNull PlaybackException error) {
|
||||
inErrorState = true;
|
||||
if (isBehindLiveWindow(error)) {
|
||||
ExoPlayable.super.reset();
|
||||
@ -180,7 +157,8 @@ public class ExoPlayable extends PlayableImpl {
|
||||
super.onPlayerError(error);
|
||||
}
|
||||
|
||||
@Override public void onPositionDiscontinuity(int reason) {
|
||||
@Override
|
||||
public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) {
|
||||
if (inErrorState) {
|
||||
// Adapt from ExoPlayer demo.
|
||||
// "This will only occur if the user has performed a seek whilst in the error state. Update
|
||||
@ -188,14 +166,13 @@ public class ExoPlayable extends PlayableImpl {
|
||||
// position to which they seek." - ExoPlayer
|
||||
ExoPlayable.super.updatePlaybackInfo();
|
||||
}
|
||||
|
||||
super.onPositionDiscontinuity(reason);
|
||||
super.onPositionDiscontinuity(oldPosition, newPosition, reason);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isBehindLiveWindow(ExoPlaybackException error) {
|
||||
if (error.type != ExoPlaybackException.TYPE_SOURCE) return false;
|
||||
Throwable cause = error.getSourceException();
|
||||
static boolean isBehindLiveWindow(PlaybackException error) {
|
||||
if (error instanceof ExoPlaybackException && ((ExoPlaybackException) error).type != ExoPlaybackException.TYPE_SOURCE) return false;
|
||||
Throwable cause = error.getCause();
|
||||
while (cause != null) {
|
||||
if (cause instanceof BehindLiveWindowException) return true;
|
||||
cause = cause.getCause();
|
||||
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Nam Nguyen, nam@ene.im
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ml.docilealligator.infinityforreddit.videoautoplay;
|
||||
|
||||
import com.google.android.exoplayer2.DefaultControlDispatcher;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.annotations.Beta;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.widget.PressablePlayerSelector;
|
||||
|
||||
/**
|
||||
* @author eneim (2018/08/18).
|
||||
* @since 3.6.0.2802
|
||||
*
|
||||
* Work with {@link PressablePlayerSelector} and {@link PlayerView} to handle user's custom playback
|
||||
* interaction. A common use-case is when user clicks the Play button to manually start a playback.
|
||||
* We should respect this by putting the {@link ToroPlayer}'s priority to highest, and request a
|
||||
* refresh for all {@link ToroPlayer}.
|
||||
*
|
||||
* The same behaviour should be handled for the case user clicks the Pause button.
|
||||
*
|
||||
* All behaviour should be cleared once user scroll the selection out of playable region. This is
|
||||
* already handled by {@link PressablePlayerSelector}.
|
||||
*/
|
||||
@Beta //
|
||||
public class ExoPlayerDispatcher extends DefaultControlDispatcher {
|
||||
|
||||
private final PressablePlayerSelector playerSelector;
|
||||
private final ToroPlayer toroPlayer;
|
||||
|
||||
public ExoPlayerDispatcher(PressablePlayerSelector playerSelector, ToroPlayer toroPlayer) {
|
||||
this.playerSelector = playerSelector;
|
||||
this.toroPlayer = toroPlayer;
|
||||
}
|
||||
|
||||
@Override public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) {
|
||||
if (playWhenReady) {
|
||||
// Container will handle the call to play.
|
||||
return playerSelector.toPlay(toroPlayer.getPlayerOrder());
|
||||
} else {
|
||||
player.setPlayWhenReady(false);
|
||||
playerSelector.toPause(toroPlayer.getPlayerOrder());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.C.ContentType;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.source.LoopingMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||
@ -46,14 +47,16 @@ import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
||||
public interface MediaSourceBuilder {
|
||||
|
||||
@NonNull MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri,
|
||||
@NonNull
|
||||
MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri,
|
||||
@Nullable String fileExt, @Nullable Handler handler,
|
||||
@NonNull DataSource.Factory manifestDataSourceFactory,
|
||||
@NonNull DataSource.Factory mediaDataSourceFactory,
|
||||
@Nullable MediaSourceEventListener listener);
|
||||
|
||||
MediaSourceBuilder DEFAULT = new MediaSourceBuilder() {
|
||||
@NonNull @Override
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri,
|
||||
@Nullable String ext, @Nullable Handler handler,
|
||||
@NonNull DataSource.Factory manifestDataSourceFactory,
|
||||
@ -61,23 +64,23 @@ public interface MediaSourceBuilder {
|
||||
@ContentType int type = isEmpty(ext) ? inferContentType(uri) : inferContentType("." + ext);
|
||||
MediaSource result;
|
||||
switch (type) {
|
||||
case C.TYPE_SS:
|
||||
case C.CONTENT_TYPE_SS:
|
||||
result = new SsMediaSource.Factory(
|
||||
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), manifestDataSourceFactory)
|
||||
.createMediaSource(uri);
|
||||
.createMediaSource(MediaItem.fromUri(uri));
|
||||
break;
|
||||
case C.TYPE_DASH:
|
||||
case C.CONTENT_TYPE_DASH:
|
||||
result = new DashMediaSource.Factory(
|
||||
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), manifestDataSourceFactory)
|
||||
.createMediaSource(uri);
|
||||
.createMediaSource(MediaItem.fromUri(uri));
|
||||
break;
|
||||
case C.TYPE_HLS:
|
||||
case C.CONTENT_TYPE_HLS:
|
||||
result = new HlsMediaSource.Factory(mediaDataSourceFactory) //
|
||||
.createMediaSource(uri);
|
||||
.createMediaSource(MediaItem.fromUri(uri));
|
||||
break;
|
||||
case C.TYPE_OTHER:
|
||||
case C.CONTENT_TYPE_OTHER:
|
||||
result = new ProgressiveMediaSource.Factory(mediaDataSourceFactory) //
|
||||
.createMediaSource(uri);
|
||||
.createMediaSource(MediaItem.fromUri(uri));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
@ -90,7 +93,8 @@ public interface MediaSourceBuilder {
|
||||
|
||||
MediaSourceBuilder LOOPING = new MediaSourceBuilder() {
|
||||
|
||||
@NonNull @Override
|
||||
@NonNull
|
||||
@Override
|
||||
public MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri,
|
||||
@Nullable String fileExt, @Nullable Handler handler,
|
||||
@NonNull DataSource.Factory manifestDataSourceFactory,
|
||||
|
@ -20,20 +20,20 @@ import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.PlaybackException;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.MetadataOutput;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.CueGroup;
|
||||
import com.google.android.exoplayer2.text.TextOutput;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.video.VideoListener;
|
||||
import com.google.android.exoplayer2.video.VideoSize;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@ -44,7 +44,7 @@ import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
|
||||
|
||||
/**
|
||||
* Define an interface to control a playback, specific for {@link SimpleExoPlayer} and {@link PlayerView}.
|
||||
*
|
||||
* <p>
|
||||
* This interface is designed to be reused across Config change. Implementation must not hold any
|
||||
* strong reference to Activity, and if it supports any kind of that, make sure to implicitly clean
|
||||
* it up.
|
||||
@ -62,7 +62,7 @@ public interface Playable {
|
||||
* - Configure {@link EventListener} for it.
|
||||
* - If there is non-trivial PlaybackInfo, update it to the SimpleExoPlayer.
|
||||
* - If client request to prepare MediaSource, then prepare it.
|
||||
*
|
||||
* <p>
|
||||
* This method must be called before {@link #setPlayerView(PlayerView)}.
|
||||
*
|
||||
* @param prepareSource if {@code true}, also prepare the MediaSource when preparing the Player,
|
||||
@ -74,7 +74,7 @@ public interface Playable {
|
||||
* Set the {@link PlayerView} for this Playable. It is expected that a playback doesn't require a
|
||||
* UI, so this setup is optional. But it must be called after the SimpleExoPlayer is prepared,
|
||||
* that is after {@link #prepare(boolean)} and before {@link #release()}.
|
||||
*
|
||||
* <p>
|
||||
* Changing the PlayerView during playback is expected, though not always recommended, especially
|
||||
* on old Devices with low Android API.
|
||||
*
|
||||
@ -87,7 +87,8 @@ public interface Playable {
|
||||
*
|
||||
* @return current PlayerView instance of this Playable.
|
||||
*/
|
||||
@Nullable PlayerView getPlayerView();
|
||||
@Nullable
|
||||
PlayerView getPlayerView();
|
||||
|
||||
/**
|
||||
* Start the playback. If the {@link MediaSource} is not prepared, then also prepare it.
|
||||
@ -163,7 +164,9 @@ public interface Playable {
|
||||
* @param volume the volume value to be set. Must be a {@code float} of range from 0 to 1.
|
||||
* @deprecated use {@link #setVolumeInfo(VolumeInfo)} instead.
|
||||
*/
|
||||
@RemoveIn(version = "3.6.0") @Deprecated //
|
||||
@RemoveIn(version = "3.6.0")
|
||||
@Deprecated
|
||||
//
|
||||
void setVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
|
||||
|
||||
/**
|
||||
@ -172,8 +175,10 @@ public interface Playable {
|
||||
* @return current volume value.
|
||||
* @deprecated use {@link #getVolumeInfo()} instead.
|
||||
*/
|
||||
@RemoveIn(version = "3.6.0") @Deprecated //
|
||||
@FloatRange(from = 0.0, to = 1.0) float getVolume();
|
||||
@RemoveIn(version = "3.6.0")
|
||||
@Deprecated //
|
||||
@FloatRange(from = 0.0, to = 1.0)
|
||||
float getVolume();
|
||||
|
||||
/**
|
||||
* Update playback's volume.
|
||||
@ -186,7 +191,8 @@ public interface Playable {
|
||||
/**
|
||||
* Get current {@link VolumeInfo}.
|
||||
*/
|
||||
@NonNull VolumeInfo getVolumeInfo();
|
||||
@NonNull
|
||||
VolumeInfo getVolumeInfo();
|
||||
|
||||
/**
|
||||
* Same as {@link Player#setPlaybackParameters(PlaybackParameters)}
|
||||
@ -196,167 +202,201 @@ public interface Playable {
|
||||
/**
|
||||
* Same as {@link Player#getPlaybackParameters()}
|
||||
*/
|
||||
@Nullable PlaybackParameters getParameters();
|
||||
@Nullable
|
||||
PlaybackParameters getParameters();
|
||||
|
||||
void addErrorListener(@NonNull ToroPlayer.OnErrorListener listener);
|
||||
|
||||
void removeErrorListener(@Nullable ToroPlayer.OnErrorListener listener);
|
||||
|
||||
// Combine necessary interfaces.
|
||||
interface EventListener extends Player.EventListener, VideoListener, TextOutput, MetadataOutput {
|
||||
interface EventListener extends Player.Listener, TextOutput, MetadataOutput {
|
||||
|
||||
}
|
||||
|
||||
/** Default empty implementation */
|
||||
class DefaultEventListener implements EventListener {
|
||||
|
||||
@Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
|
||||
@Override
|
||||
default void onCues(@NonNull List<Cue> cues) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
default void onCues(@NonNull CueGroup cueGroup) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onLoadingChanged(boolean isLoading) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onRepeatModeChanged(int repeatMode) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onPlayerError(ExoPlaybackException error) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onPositionDiscontinuity(int reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onSeekProcessed() {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
|
||||
float pixelWidthHeightRatio) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onRenderedFirstFrame() {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onCues(List<Cue> cues) {
|
||||
|
||||
}
|
||||
|
||||
@Override public void onMetadata(Metadata metadata) {
|
||||
@Override
|
||||
default void onMetadata(@NonNull Metadata metadata) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** List of EventListener */
|
||||
/**
|
||||
* Default empty implementation
|
||||
*/
|
||||
class DefaultEventListener implements EventListener {
|
||||
@Override
|
||||
public void onTimelineChanged(@NonNull Timeline timeline, int reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsLoadingChanged(boolean isLoading) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(int playbackState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatModeChanged(int repeatMode) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(@NonNull PlaybackException error) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(@NonNull PlaybackParameters playbackParameters) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSeekProcessed() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
|
||||
EventListener.super.onVideoSizeChanged(videoSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCues(@NonNull CueGroup cueGroup) {
|
||||
EventListener.super.onCues(cueGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadata(@NonNull Metadata metadata) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of EventListener
|
||||
*/
|
||||
class EventListeners extends CopyOnWriteArraySet<EventListener> implements EventListener {
|
||||
|
||||
EventListeners() {
|
||||
}
|
||||
|
||||
@Override public void onVideoSizeChanged(int width, int height, int unAppliedRotationDegrees,
|
||||
float pixelWidthHeightRatio) {
|
||||
@Override
|
||||
public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onVideoSizeChanged(width, height, unAppliedRotationDegrees,
|
||||
pixelWidthHeightRatio);
|
||||
eventListener.onVideoSizeChanged(videoSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onRenderedFirstFrame() {
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onRenderedFirstFrame();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
|
||||
@Override
|
||||
public void onTimelineChanged(@NonNull Timeline timeline, int reason) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onTimelineChanged(timeline, manifest, reason);
|
||||
eventListener.onTimelineChanged(timeline, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
|
||||
public void onTracksChanged(@NonNull Tracks tracks) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onTracksChanged(trackGroups, trackSelections);
|
||||
eventListener.onTracksChanged(tracks);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onLoadingChanged(boolean isLoading) {
|
||||
@Override
|
||||
public void onIsLoadingChanged(boolean isLoading) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onLoadingChanged(isLoading);
|
||||
eventListener.onIsLoadingChanged(isLoading);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
@Override
|
||||
public void onPlaybackStateChanged(int playbackState) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
eventListener.onPlaybackStateChanged(playbackState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onRepeatModeChanged(int repeatMode) {
|
||||
@Override
|
||||
public void onRepeatModeChanged(int repeatMode) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onRepeatModeChanged(repeatMode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
@Override
|
||||
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPlayerError(ExoPlaybackException error) {
|
||||
@Override
|
||||
public void onPlayerError(@NonNull PlaybackException error) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onPlayerError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPositionDiscontinuity(int reason) {
|
||||
@Override
|
||||
public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onPositionDiscontinuity(reason);
|
||||
eventListener.onPositionDiscontinuity(oldPosition, newPosition, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
|
||||
@Override
|
||||
public void onPlaybackParametersChanged(@NonNull PlaybackParameters playbackParameters) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onPlaybackParametersChanged(playbackParameters);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onSeekProcessed() {
|
||||
@Override
|
||||
public void onCues(@NonNull CueGroup cueGroup) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onSeekProcessed();
|
||||
eventListener.onCues(cueGroup);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onCues(List<Cue> cues) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onCues(cues);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void onMetadata(Metadata metadata) {
|
||||
@Override
|
||||
public void onMetadata(@NonNull Metadata metadata) {
|
||||
for (EventListener eventListener : this) {
|
||||
eventListener.onMetadata(metadata);
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
|
||||
@ -39,9 +38,9 @@ import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
|
||||
|
||||
/**
|
||||
* [20180225]
|
||||
*
|
||||
* <p>
|
||||
* Default implementation of {@link Playable}.
|
||||
*
|
||||
* <p>
|
||||
* Instance of {@link Playable} should be reusable. Retaining instance of Playable across config
|
||||
* change must guarantee that all {@link EventListener} are cleaned up on config change.
|
||||
*
|
||||
@ -60,7 +59,7 @@ class PlayableImpl implements Playable {
|
||||
protected final String fileExt;
|
||||
protected final ExoCreator creator; // required, cached
|
||||
|
||||
protected SimpleExoPlayer player; // on-demand, cached
|
||||
protected ToroExoPlayer player; // on-demand, cached
|
||||
protected MediaSource mediaSource; // on-demand, since we do not reuse MediaSource now.
|
||||
protected PlayerView playerView; // on-demand, not always required.
|
||||
|
||||
@ -73,84 +72,101 @@ class PlayableImpl implements Playable {
|
||||
this.fileExt = fileExt;
|
||||
}
|
||||
|
||||
@CallSuper @Override public void prepare(boolean prepareSource) {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void prepare(boolean prepareSource) {
|
||||
if (prepareSource) {
|
||||
ensureMediaSource();
|
||||
ensurePlayerView();
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper @Override public void setPlayerView(@Nullable PlayerView playerView) {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void setPlayerView(@Nullable PlayerView playerView) {
|
||||
if (this.playerView == playerView) return;
|
||||
if (playerView == null) {
|
||||
this.playerView.setPlayer(null);
|
||||
} else {
|
||||
if (this.player != null) {
|
||||
PlayerView.switchTargetView(this.player, this.playerView, playerView);
|
||||
PlayerView.switchTargetView(this.player.getPlayer(), this.playerView, playerView);
|
||||
}
|
||||
}
|
||||
|
||||
this.playerView = playerView;
|
||||
}
|
||||
|
||||
@Override public final PlayerView getPlayerView() {
|
||||
@Override
|
||||
public final PlayerView getPlayerView() {
|
||||
return this.playerView;
|
||||
}
|
||||
|
||||
@CallSuper @Override public void play() {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void play() {
|
||||
ensureMediaSource();
|
||||
ensurePlayerView();
|
||||
checkNotNull(player, "Playable#play(): Player is null!");
|
||||
player.setPlayWhenReady(true);
|
||||
player.getPlayer().setPlayWhenReady(true);
|
||||
}
|
||||
|
||||
@CallSuper @Override public void pause() {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void pause() {
|
||||
// Player is not required to be non-null here.
|
||||
if (player != null) player.setPlayWhenReady(false);
|
||||
if (player != null) player.getPlayer().setPlayWhenReady(false);
|
||||
}
|
||||
|
||||
@CallSuper @Override public void reset() {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void reset() {
|
||||
this.playbackInfo.reset();
|
||||
if (player != null) {
|
||||
// reset volume to default
|
||||
ToroExo.setVolumeInfo(this.player, new VolumeInfo(false, 1.f));
|
||||
player.stop(true);
|
||||
player.getPlayer().stop();
|
||||
player.getPlayer().clearMediaItems();
|
||||
}
|
||||
this.mediaSource = null; // so it will be re-prepared when play() is called.
|
||||
this.sourcePrepared = false;
|
||||
}
|
||||
|
||||
@CallSuper @Override public void release() {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void release() {
|
||||
this.setPlayerView(null);
|
||||
if (this.player != null) {
|
||||
// reset volume to default
|
||||
ToroExo.setVolumeInfo(this.player, new VolumeInfo(false, 1.f));
|
||||
this.player.stop(true);
|
||||
player.getPlayer().stop();
|
||||
player.getPlayer().clearMediaItems();
|
||||
if (listenerApplied) {
|
||||
player.removeListener(listeners);
|
||||
player.removeVideoListener(listeners);
|
||||
player.removeTextOutput(listeners);
|
||||
player.removeMetadataOutput(listeners);
|
||||
if (this.player instanceof ToroExoPlayer) {
|
||||
((ToroExoPlayer) this.player).removeOnVolumeChangeListener(this.volumeChangeListeners);
|
||||
player.getPlayer().removeListener(listeners);
|
||||
if (this.player != null) {
|
||||
this.player.removeOnVolumeChangeListener(this.volumeChangeListeners);
|
||||
}
|
||||
listenerApplied = false;
|
||||
}
|
||||
with(checkNotNull(creator.getContext(), "ExoCreator has no Context")) //
|
||||
.releasePlayer(this.creator, this.player);
|
||||
.releasePlayer(this.creator, this.player.getPlayer());
|
||||
}
|
||||
this.player = null;
|
||||
this.mediaSource = null;
|
||||
this.sourcePrepared = false;
|
||||
}
|
||||
|
||||
@CallSuper @NonNull @Override public PlaybackInfo getPlaybackInfo() {
|
||||
@CallSuper
|
||||
@NonNull
|
||||
@Override
|
||||
public PlaybackInfo getPlaybackInfo() {
|
||||
updatePlaybackInfo();
|
||||
return new PlaybackInfo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition(),
|
||||
playbackInfo.getVolumeInfo());
|
||||
}
|
||||
|
||||
@CallSuper @Override public void setPlaybackInfo(@NonNull PlaybackInfo playbackInfo) {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void setPlaybackInfo(@NonNull PlaybackInfo playbackInfo) {
|
||||
this.playbackInfo.setResumeWindow(playbackInfo.getResumeWindow());
|
||||
this.playbackInfo.setResumePosition(playbackInfo.getResumePosition());
|
||||
this.setVolumeInfo(playbackInfo.getVolumeInfo());
|
||||
@ -159,31 +175,38 @@ class PlayableImpl implements Playable {
|
||||
ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo());
|
||||
boolean haveResumePosition = this.playbackInfo.getResumeWindow() != INDEX_UNSET;
|
||||
if (haveResumePosition) {
|
||||
player.seekTo(this.playbackInfo.getResumeWindow(), this.playbackInfo.getResumePosition());
|
||||
player.getPlayer().seekTo(this.playbackInfo.getResumeWindow(), this.playbackInfo.getResumePosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override public final void addEventListener(@NonNull EventListener listener) {
|
||||
@Override
|
||||
public final void addEventListener(@NonNull EventListener listener) {
|
||||
//noinspection ConstantConditions
|
||||
if (listener != null) this.listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override public final void removeEventListener(EventListener listener) {
|
||||
@Override
|
||||
public final void removeEventListener(EventListener listener) {
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
@CallSuper @Override public void setVolume(float volume) {
|
||||
@CallSuper
|
||||
@Override
|
||||
public void setVolume(float volume) {
|
||||
checkNotNull(player, "Playable#setVolume(): Player is null!");
|
||||
playbackInfo.getVolumeInfo().setTo(volume == 0, volume);
|
||||
ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo());
|
||||
}
|
||||
|
||||
@CallSuper @Override public float getVolume() {
|
||||
return checkNotNull(player, "Playable#getVolume(): Player is null!").getVolume();
|
||||
@CallSuper
|
||||
@Override
|
||||
public float getVolume() {
|
||||
return checkNotNull(player.getPlayer(), "Playable#getVolume(): Player is null!").getVolume();
|
||||
}
|
||||
|
||||
@Override public boolean setVolumeInfo(@NonNull VolumeInfo volumeInfo) {
|
||||
@Override
|
||||
public boolean setVolumeInfo(@NonNull VolumeInfo volumeInfo) {
|
||||
boolean changed = !this.playbackInfo.getVolumeInfo().equals(checkNotNull(volumeInfo));
|
||||
if (changed) {
|
||||
this.playbackInfo.getVolumeInfo().setTo(volumeInfo.isMute(), volumeInfo.getVolume());
|
||||
@ -192,17 +215,21 @@ class PlayableImpl implements Playable {
|
||||
return changed;
|
||||
}
|
||||
|
||||
@NonNull @Override public VolumeInfo getVolumeInfo() {
|
||||
@NonNull
|
||||
@Override
|
||||
public VolumeInfo getVolumeInfo() {
|
||||
return this.playbackInfo.getVolumeInfo();
|
||||
}
|
||||
|
||||
@Override public void setParameters(@Nullable PlaybackParameters parameters) {
|
||||
checkNotNull(player, "Playable#setParameters(PlaybackParameters): Player is null") //
|
||||
@Override
|
||||
public void setParameters(@Nullable PlaybackParameters parameters) {
|
||||
checkNotNull(player.getPlayer(), "Playable#setParameters(PlaybackParameters): Player is null") //
|
||||
.setPlaybackParameters(parameters);
|
||||
}
|
||||
|
||||
@Override public PlaybackParameters getParameters() {
|
||||
return checkNotNull(player, "Playable#getParameters(): Player is null").getPlaybackParameters();
|
||||
@Override
|
||||
public PlaybackParameters getParameters() {
|
||||
return checkNotNull(player.getPlayer(), "Playable#getParameters(): Player is null").getPlaybackParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -215,28 +242,31 @@ class PlayableImpl implements Playable {
|
||||
volumeChangeListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override public boolean isPlaying() {
|
||||
return player != null && player.getPlayWhenReady();
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return player != null && player.getPlayer().getPlayWhenReady();
|
||||
}
|
||||
|
||||
@Override public void addErrorListener(@NonNull ToroPlayer.OnErrorListener listener) {
|
||||
@Override
|
||||
public void addErrorListener(@NonNull ToroPlayer.OnErrorListener listener) {
|
||||
this.errorListeners.add(checkNotNull(listener));
|
||||
}
|
||||
|
||||
@Override public void removeErrorListener(@Nullable ToroPlayer.OnErrorListener listener) {
|
||||
@Override
|
||||
public void removeErrorListener(@Nullable ToroPlayer.OnErrorListener listener) {
|
||||
this.errorListeners.remove(listener);
|
||||
}
|
||||
|
||||
final void updatePlaybackInfo() {
|
||||
if (player == null || player.getPlaybackState() == Player.STATE_IDLE) return;
|
||||
playbackInfo.setResumeWindow(player.getCurrentWindowIndex());
|
||||
playbackInfo.setResumePosition(player.isCurrentWindowSeekable() ? //
|
||||
Math.max(0, player.getCurrentPosition()) : TIME_UNSET);
|
||||
if (player == null || player.getPlayer().getPlaybackState() == Player.STATE_IDLE) return;
|
||||
playbackInfo.setResumeWindow(player.getPlayer().getCurrentWindowIndex());
|
||||
playbackInfo.setResumePosition(player.getPlayer().isCurrentWindowSeekable() ? //
|
||||
Math.max(0, player.getPlayer().getCurrentPosition()) : TIME_UNSET);
|
||||
playbackInfo.setVolumeInfo(ToroExo.getVolumeInfo(player));
|
||||
}
|
||||
|
||||
private void ensurePlayerView() {
|
||||
if (playerView != null && playerView.getPlayer() != player) playerView.setPlayer(player);
|
||||
if (playerView != null && playerView.getPlayer() != player.getPlayer()) playerView.setPlayer(player.getPlayer());
|
||||
}
|
||||
|
||||
// TODO [20180822] Double check this.
|
||||
@ -249,7 +279,7 @@ class PlayableImpl implements Playable {
|
||||
if (!sourcePrepared) {
|
||||
ensurePlayer(); // sourcePrepared is set to false only when player is null.
|
||||
beforePrepareMediaSource();
|
||||
player.prepare(mediaSource, playbackInfo.getResumeWindow() == C.INDEX_UNSET, false);
|
||||
player.getPlayer().prepare(mediaSource, playbackInfo.getResumeWindow() == C.INDEX_UNSET, false);
|
||||
sourcePrepared = true;
|
||||
}
|
||||
}
|
||||
@ -263,20 +293,15 @@ class PlayableImpl implements Playable {
|
||||
}
|
||||
|
||||
if (!listenerApplied) {
|
||||
if (player instanceof ToroExoPlayer) {
|
||||
((ToroExoPlayer) player).addOnVolumeChangeListener(volumeChangeListeners);
|
||||
}
|
||||
player.addListener(listeners);
|
||||
player.addVideoListener(listeners);
|
||||
player.addTextOutput(listeners);
|
||||
player.addMetadataOutput(listeners);
|
||||
player.addOnVolumeChangeListener(volumeChangeListeners);
|
||||
player.getPlayer().addListener(listeners);
|
||||
listenerApplied = true;
|
||||
}
|
||||
|
||||
ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo());
|
||||
boolean haveResumePosition = playbackInfo.getResumeWindow() != C.INDEX_UNSET;
|
||||
if (haveResumePosition) {
|
||||
player.seekTo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition());
|
||||
player.getPlayer().seekTo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,34 +16,21 @@
|
||||
|
||||
package ml.docilealligator.infinityforreddit.videoautoplay;
|
||||
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static com.google.android.exoplayer2.drm.UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME;
|
||||
import static com.google.android.exoplayer2.util.Util.getDrmUuid;
|
||||
import static java.lang.Runtime.getRuntime;
|
||||
import static ml.docilealligator.infinityforreddit.videoautoplay.ToroUtil.checkNotNull;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.util.Pools;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaDrm;
|
||||
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
||||
import com.google.android.exoplayer2.drm.UnsupportedDrmException;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
||||
import java.net.CookieHandler;
|
||||
@ -52,18 +39,15 @@ import java.net.CookiePolicy;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.R;
|
||||
import ml.docilealligator.infinityforreddit.utils.APIUtils;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.media.DrmMedia;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
|
||||
|
||||
/**
|
||||
* Global helper class to manage {@link ExoCreator} and {@link SimpleExoPlayer} instances.
|
||||
* In this setup, {@link ExoCreator} and SimpleExoPlayer pools are cached. A {@link Config}
|
||||
* is a key for each {@link ExoCreator}.
|
||||
*
|
||||
* <p>
|
||||
* A suggested usage is as below:
|
||||
* <pre><code>
|
||||
* ExoCreator creator = ToroExo.with(this).getDefaultCreator();
|
||||
@ -95,10 +79,14 @@ public final class ToroExo {
|
||||
return toro;
|
||||
}
|
||||
|
||||
@NonNull final String appName;
|
||||
@NonNull final Context context; // Application context
|
||||
@NonNull private final Map<Config, ExoCreator> creators;
|
||||
@NonNull private final Map<ExoCreator, Pools.Pool<SimpleExoPlayer>> playerPools;
|
||||
@NonNull
|
||||
final String appName;
|
||||
@NonNull
|
||||
final Context context; // Application context
|
||||
@NonNull
|
||||
private final Map<Config, ExoCreator> creators;
|
||||
@NonNull
|
||||
private final Map<ExoCreator, Pools.Pool<ExoPlayer>> playerPools;
|
||||
|
||||
private Config defaultConfig; // will be created on the first time it is used.
|
||||
|
||||
@ -129,7 +117,8 @@ public final class ToroExo {
|
||||
return creator;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") public final Config getDefaultConfig() {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public final Config getDefaultConfig() {
|
||||
if (defaultConfig == null) defaultConfig = new Config.Builder(context).build();
|
||||
return defaultConfig;
|
||||
}
|
||||
@ -144,7 +133,7 @@ public final class ToroExo {
|
||||
/**
|
||||
* Request an instance of {@link SimpleExoPlayer}. It can be an existing instance cached by Pool
|
||||
* or new one.
|
||||
*
|
||||
* <p>
|
||||
* The creator may or may not be the one created by either {@link #getCreator(Config)} or
|
||||
* {@link #getDefaultCreator()}.
|
||||
*
|
||||
@ -152,10 +141,10 @@ public final class ToroExo {
|
||||
* @return an usable {@link SimpleExoPlayer} instance.
|
||||
*/
|
||||
@NonNull //
|
||||
public final SimpleExoPlayer requestPlayer(@NonNull ExoCreator creator) {
|
||||
SimpleExoPlayer player = getPool(checkNotNull(creator)).acquire();
|
||||
public final ToroExoPlayer requestPlayer(@NonNull ExoCreator creator) {
|
||||
ExoPlayer player = getPool(checkNotNull(creator)).acquire();
|
||||
if (player == null) player = creator.createPlayer();
|
||||
return player;
|
||||
return new ToroExoPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,7 +155,7 @@ public final class ToroExo {
|
||||
* @return true if player is released to relevant Pool, false otherwise.
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) //
|
||||
public final boolean releasePlayer(@NonNull ExoCreator creator, @NonNull SimpleExoPlayer player) {
|
||||
public final boolean releasePlayer(@NonNull ExoCreator creator, @NonNull ExoPlayer player) {
|
||||
return getPool(checkNotNull(creator)).release(player);
|
||||
}
|
||||
|
||||
@ -176,18 +165,18 @@ public final class ToroExo {
|
||||
*/
|
||||
public final void cleanUp() {
|
||||
// TODO [2018/03/07] Test this. Ref: https://stackoverflow.com/a/1884916/1553254
|
||||
for (Iterator<Map.Entry<ExoCreator, Pools.Pool<SimpleExoPlayer>>> it =
|
||||
for (Iterator<Map.Entry<ExoCreator, Pools.Pool<ExoPlayer>>> it =
|
||||
playerPools.entrySet().iterator(); it.hasNext(); ) {
|
||||
Pools.Pool<SimpleExoPlayer> pool = it.next().getValue();
|
||||
SimpleExoPlayer item;
|
||||
Pools.Pool<ExoPlayer> pool = it.next().getValue();
|
||||
ExoPlayer item;
|
||||
while ((item = pool.acquire()) != null) item.release();
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/// internal APIs
|
||||
private Pools.Pool<SimpleExoPlayer> getPool(ExoCreator creator) {
|
||||
Pools.Pool<SimpleExoPlayer> pool = playerPools.get(creator);
|
||||
private Pools.Pool<ExoPlayer> getPool(ExoCreator creator) {
|
||||
Pools.Pool<ExoPlayer> pool = playerPools.get(creator);
|
||||
if (pool == null) {
|
||||
pool = new Pools.SimplePool<>(MAX_POOL_SIZE);
|
||||
playerPools.put(creator, pool);
|
||||
@ -204,90 +193,17 @@ public final class ToroExo {
|
||||
this.context.getString(resId) : this.context.getString(resId, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to build a {@link DrmSessionManager} that can be used in {@link Config}
|
||||
*
|
||||
* Usage:
|
||||
* <pre><code>
|
||||
* DrmSessionManager manager = ToroExo.with(context).createDrmSessionManager(mediaDrm);
|
||||
* Config config = new Config.Builder().setDrmSessionManager(manager);
|
||||
* ExoCreator creator = ToroExo.with(context).getCreator(config);
|
||||
* </code></pre>
|
||||
*/
|
||||
@SuppressWarnings("unused") @RequiresApi(18) @Nullable //
|
||||
public DrmSessionManager<FrameworkMediaCrypto> createDrmSessionManager(@NonNull DrmMedia drm) {
|
||||
DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = null;
|
||||
int errorStringId = R.string.error_drm_unknown;
|
||||
String subString = null;
|
||||
if (Util.SDK_INT < 18) {
|
||||
errorStringId = R.string.error_drm_not_supported;
|
||||
} else {
|
||||
UUID drmSchemeUuid = getDrmUuid(checkNotNull(drm).getType());
|
||||
if (drmSchemeUuid == null) {
|
||||
errorStringId = R.string.error_drm_unsupported_scheme;
|
||||
} else {
|
||||
HttpDataSource.Factory factory = new DefaultHttpDataSourceFactory(appName);
|
||||
try {
|
||||
drmSessionManager = buildDrmSessionManagerV18(drmSchemeUuid, drm.getLicenseUrl(),
|
||||
drm.getKeyRequestPropertiesArray(), drm.multiSession(), factory);
|
||||
} catch (UnsupportedDrmException e) {
|
||||
e.printStackTrace();
|
||||
errorStringId = e.reason == REASON_UNSUPPORTED_SCHEME ? //
|
||||
R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown;
|
||||
if (e.reason == REASON_UNSUPPORTED_SCHEME) {
|
||||
subString = drm.getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drmSessionManager == null) {
|
||||
String error = TextUtils.isEmpty(subString) ? context.getString(errorStringId)
|
||||
: context.getString(errorStringId) + ": " + subString;
|
||||
Toast.makeText(context, error, LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
return drmSessionManager;
|
||||
}
|
||||
|
||||
@RequiresApi(18) private static DrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManagerV18(
|
||||
@NonNull UUID uuid, @Nullable String licenseUrl, @Nullable String[] keyRequestPropertiesArray,
|
||||
boolean multiSession, @NonNull HttpDataSource.Factory httpDataSourceFactory)
|
||||
throws UnsupportedDrmException {
|
||||
HttpMediaDrmCallback drmCallback = new HttpMediaDrmCallback(licenseUrl, httpDataSourceFactory);
|
||||
if (keyRequestPropertiesArray != null) {
|
||||
for (int i = 0; i < keyRequestPropertiesArray.length - 1; i += 2) {
|
||||
drmCallback.setKeyRequestProperty(keyRequestPropertiesArray[i],
|
||||
keyRequestPropertiesArray[i + 1]);
|
||||
}
|
||||
}
|
||||
return new DefaultDrmSessionManager<>(uuid, FrameworkMediaDrm.newInstance(uuid), drmCallback,
|
||||
null, multiSession);
|
||||
}
|
||||
|
||||
// Share the code of setting Volume. For use inside library only.
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) //
|
||||
public static void setVolumeInfo(@NonNull SimpleExoPlayer player,
|
||||
public static void setVolumeInfo(@NonNull ToroExoPlayer player,
|
||||
@NonNull VolumeInfo volumeInfo) {
|
||||
if (player instanceof ToroExoPlayer) {
|
||||
((ToroExoPlayer) player).setVolumeInfo(volumeInfo);
|
||||
} else {
|
||||
if (volumeInfo.isMute()) {
|
||||
player.setVolume(0f);
|
||||
} else {
|
||||
player.setVolume(volumeInfo.getVolume());
|
||||
}
|
||||
}
|
||||
player.setVolumeInfo(volumeInfo);
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) //
|
||||
public static VolumeInfo getVolumeInfo(SimpleExoPlayer player) {
|
||||
if (player instanceof ToroExoPlayer) {
|
||||
return new VolumeInfo(((ToroExoPlayer) player).getVolumeInfo());
|
||||
} else {
|
||||
float volume = player.getVolume();
|
||||
return new VolumeInfo(volume == 0, volume);
|
||||
}
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) //
|
||||
public static VolumeInfo getVolumeInfo(ToroExoPlayer player) {
|
||||
return new VolumeInfo(player.getVolumeInfo());
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
|
@ -23,13 +23,11 @@ import android.os.Looper;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.LoadControl;
|
||||
import com.google.android.exoplayer2.RenderersFactory;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||
|
||||
@ -41,13 +39,18 @@ import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
|
||||
* @author eneim (2018/03/27).
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess") //
|
||||
public class ToroExoPlayer extends SimpleExoPlayer {
|
||||
public class ToroExoPlayer {
|
||||
|
||||
protected ToroExoPlayer(Context context, RenderersFactory renderersFactory,
|
||||
private ExoPlayer player;
|
||||
|
||||
public ToroExoPlayer(Context context, RenderersFactory renderersFactory,
|
||||
TrackSelector trackSelector, LoadControl loadControl, BandwidthMeter bandwidthMeter,
|
||||
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, Looper looper) {
|
||||
super(context, renderersFactory, trackSelector, loadControl, bandwidthMeter, drmSessionManager,
|
||||
looper);
|
||||
Looper looper) {
|
||||
player = new ExoPlayer.Builder(context).setRenderersFactory(renderersFactory).setTrackSelector(trackSelector).setLoadControl(loadControl).setBandwidthMeter(bandwidthMeter).setLooper(looper).build();
|
||||
}
|
||||
|
||||
public ToroExoPlayer(ExoPlayer exoPlayer) {
|
||||
this.player = exoPlayer;
|
||||
}
|
||||
|
||||
private ToroPlayer.VolumeChangeListeners listeners;
|
||||
@ -65,7 +68,8 @@ public class ToroExoPlayer extends SimpleExoPlayer {
|
||||
if (this.listeners != null) this.listeners.clear();
|
||||
}
|
||||
|
||||
@CallSuper @Override public void setVolume(float audioVolume) {
|
||||
@CallSuper
|
||||
public void setVolume(float audioVolume) {
|
||||
this.setVolumeInfo(new VolumeInfo(audioVolume == 0, audioVolume));
|
||||
}
|
||||
|
||||
@ -76,7 +80,7 @@ public class ToroExoPlayer extends SimpleExoPlayer {
|
||||
boolean changed = !this.volumeInfo.equals(volumeInfo);
|
||||
if (changed) {
|
||||
this.volumeInfo.setTo(volumeInfo.isMute(), volumeInfo.getVolume());
|
||||
super.setVolume(volumeInfo.isMute() ? 0 : volumeInfo.getVolume());
|
||||
player.setVolume(volumeInfo.isMute() ? 0 : volumeInfo.getVolume());
|
||||
if (listeners != null) {
|
||||
for (ToroPlayer.OnVolumeChangeListener listener : this.listeners) {
|
||||
listener.onVolumeChanged(volumeInfo);
|
||||
@ -87,7 +91,13 @@ public class ToroExoPlayer extends SimpleExoPlayer {
|
||||
return changed;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") @NonNull public final VolumeInfo getVolumeInfo() {
|
||||
@SuppressWarnings("unused")
|
||||
@NonNull
|
||||
public final VolumeInfo getVolumeInfo() {
|
||||
return volumeInfo;
|
||||
}
|
||||
|
||||
public ExoPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ import ml.docilealligator.infinityforreddit.videoautoplay.widget.Container;
|
||||
|
||||
public final class ToroUtil {
|
||||
|
||||
@SuppressWarnings("unused") private static final String TAG = "ToroLib:Util";
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "ToroLib:Util";
|
||||
|
||||
private ToroUtil() {
|
||||
throw new RuntimeException("Meh!");
|
||||
@ -76,7 +77,8 @@ public final class ToroUtil {
|
||||
* @return the non-null reference that was validated
|
||||
* @throws NullPointerException if {@code reference} is null
|
||||
*/
|
||||
public static @NonNull <T> T checkNotNull(final T reference) {
|
||||
public static @NonNull
|
||||
<T> T checkNotNull(final T reference) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
@ -93,7 +95,8 @@ public final class ToroUtil {
|
||||
* @return the non-null reference that was validated
|
||||
* @throws NullPointerException if {@code reference} is null
|
||||
*/
|
||||
public static @NonNull <T> T checkNotNull(final T reference, final Object errorMessage) {
|
||||
public static @NonNull
|
||||
<T> T checkNotNull(final T reference, final Object errorMessage) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(String.valueOf(errorMessage));
|
||||
}
|
||||
|
@ -1,278 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Nam Nguyen, nam@ene.im
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ml.docilealligator.infinityforreddit.videoautoplay.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.ui.TimeBar;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import ml.docilealligator.infinityforreddit.R;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.ToroExo;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.ToroExoPlayer;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.ToroPlayer;
|
||||
import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
|
||||
|
||||
/**
|
||||
* An extension of {@link PlayerControlView} that adds Volume control buttons. It works on-par
|
||||
* with {@link PlayerView}. Will be automatically inflated when client uses {@link R.layout.toro_exo_player_view}
|
||||
* for {@link PlayerView} layout.
|
||||
*
|
||||
* @author eneim (2018/08/20).
|
||||
* @since 3.6.0.2802
|
||||
*/
|
||||
public class ToroControlView extends PlayerControlView {
|
||||
|
||||
@SuppressWarnings("unused") static final String TAG = "ToroExo:Control";
|
||||
|
||||
// Statically obtain from super class.
|
||||
protected static Method hideAfterTimeoutMethod; // from parent ...
|
||||
protected static boolean hideMethodFetched;
|
||||
protected static Field hideActionField;
|
||||
protected static boolean hideActionFetched;
|
||||
|
||||
final ComponentListener componentListener;
|
||||
final View volumeUpButton;
|
||||
final View volumeOffButton;
|
||||
final TimeBar volumeBar;
|
||||
final VolumeInfo volumeInfo = new VolumeInfo(false, 1);
|
||||
|
||||
public ToroControlView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ToroControlView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ToroControlView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
volumeOffButton = findViewById(R.id.exo_volume_off);
|
||||
volumeUpButton = findViewById(R.id.exo_volume_up);
|
||||
volumeBar = findViewById(R.id.volume_bar);
|
||||
componentListener = new ComponentListener();
|
||||
}
|
||||
|
||||
@Override public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (volumeUpButton != null) volumeUpButton.setOnClickListener(componentListener);
|
||||
if (volumeOffButton != null) volumeOffButton.setOnClickListener(componentListener);
|
||||
if (volumeBar != null) volumeBar.addListener(componentListener);
|
||||
|
||||
updateVolumeButtons();
|
||||
}
|
||||
|
||||
@Override public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (volumeUpButton != null) volumeUpButton.setOnClickListener(null);
|
||||
if (volumeOffButton != null) volumeOffButton.setOnClickListener(null);
|
||||
if (volumeBar != null) volumeBar.removeListener(componentListener);
|
||||
this.setPlayer(null);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility") @Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// After processing all children' touch event, this View will just stop it here.
|
||||
// User can click to PlayerView to show/hide this view, but since this View's height is not
|
||||
// significantly large, clicking to show/hide may disturb other actions like clicking to button,
|
||||
// seeking the bars, etc. This extension will stop the touch event here so that PlayerView has
|
||||
// nothing to do when User touch this View.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public void setPlayer(Player player) {
|
||||
Player current = super.getPlayer();
|
||||
if (current == player) return;
|
||||
|
||||
if (current instanceof ToroExoPlayer) {
|
||||
((ToroExoPlayer) current).removeOnVolumeChangeListener(componentListener);
|
||||
}
|
||||
|
||||
super.setPlayer(player);
|
||||
current = super.getPlayer();
|
||||
@NonNull final VolumeInfo tempVol;
|
||||
if (current instanceof ToroExoPlayer) {
|
||||
tempVol = ((ToroExoPlayer) current).getVolumeInfo();
|
||||
((ToroExoPlayer) current).addOnVolumeChangeListener(componentListener);
|
||||
} else if (current instanceof SimpleExoPlayer) {
|
||||
float volume = ((SimpleExoPlayer) current).getVolume();
|
||||
tempVol = new VolumeInfo(volume == 0, volume);
|
||||
} else {
|
||||
tempVol = new VolumeInfo(false, 1f);
|
||||
}
|
||||
|
||||
this.volumeInfo.setTo(tempVol.isMute(), tempVol.getVolume());
|
||||
updateVolumeButtons();
|
||||
}
|
||||
|
||||
@Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
|
||||
super.onVisibilityChanged(changedView, visibility);
|
||||
if (changedView == this) updateVolumeButtons();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions") //
|
||||
void updateVolumeButtons() {
|
||||
if (!isVisible() || !ViewCompat.isAttachedToWindow(this)) {
|
||||
return;
|
||||
}
|
||||
boolean requestButtonFocus = false;
|
||||
// if muted then show volumeOffButton, or else show volumeUpButton
|
||||
boolean muted = volumeInfo.isMute();
|
||||
if (volumeOffButton != null) {
|
||||
requestButtonFocus |= muted && volumeOffButton.isFocused();
|
||||
volumeOffButton.setVisibility(muted ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
if (volumeUpButton != null) {
|
||||
requestButtonFocus |= !muted && volumeUpButton.isFocused();
|
||||
volumeUpButton.setVisibility(!muted ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
if (volumeBar != null) {
|
||||
volumeBar.setDuration(100);
|
||||
volumeBar.setPosition(muted ? 0 : (long) (volumeInfo.getVolume() * 100));
|
||||
}
|
||||
|
||||
if (requestButtonFocus) {
|
||||
requestButtonFocus();
|
||||
}
|
||||
|
||||
// A hack to access PlayerControlView's hideAfterTimeout. Don't want to re-implement it.
|
||||
// Reflection happens once for all instances, so it should not affect the performance.
|
||||
if (!hideMethodFetched) {
|
||||
try {
|
||||
hideAfterTimeoutMethod = PlayerControlView.class.getDeclaredMethod("hideAfterTimeout");
|
||||
hideAfterTimeoutMethod.setAccessible(true);
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
hideMethodFetched = true;
|
||||
}
|
||||
|
||||
if (hideAfterTimeoutMethod != null) {
|
||||
try {
|
||||
hideAfterTimeoutMethod.invoke(this);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requestButtonFocus() {
|
||||
boolean muted = volumeInfo.isMute();
|
||||
if (!muted && volumeUpButton != null) {
|
||||
volumeUpButton.requestFocus();
|
||||
} else if (muted && volumeOffButton != null) {
|
||||
volumeOffButton.requestFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void dispatchOnScrubStart() {
|
||||
// Fetch the 'hideAction' Runnable from super class. We need this to synchronize the show/hide
|
||||
// behaviour when user does something.
|
||||
if (!hideActionFetched) {
|
||||
try {
|
||||
hideActionField = PlayerControlView.class.getDeclaredField("hideAction");
|
||||
hideActionField.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
hideActionFetched = true;
|
||||
}
|
||||
|
||||
if (hideActionField != null) {
|
||||
try {
|
||||
removeCallbacks((Runnable) hideActionField.get(this));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrub Move will always modify actual Volume, there is no 'mute-with-non-zero-volume' state.
|
||||
void dispatchOnScrubMove(long position) {
|
||||
if (position > 100) position = 100;
|
||||
if (position < 0) position = 0;
|
||||
|
||||
float actualVolume = position / (float) 100;
|
||||
this.volumeInfo.setTo(actualVolume == 0, actualVolume);
|
||||
if (getPlayer() instanceof SimpleExoPlayer) {
|
||||
ToroExo.setVolumeInfo((SimpleExoPlayer) getPlayer(), this.volumeInfo);
|
||||
}
|
||||
|
||||
updateVolumeButtons();
|
||||
}
|
||||
|
||||
void dispatchOnScrubStop(long position) {
|
||||
this.dispatchOnScrubMove(position);
|
||||
}
|
||||
|
||||
private class ComponentListener
|
||||
implements OnClickListener, TimeBar.OnScrubListener, ToroPlayer.OnVolumeChangeListener {
|
||||
|
||||
ComponentListener() {
|
||||
}
|
||||
|
||||
@Override public void onClick(View v) {
|
||||
Player player = ToroControlView.super.getPlayer();
|
||||
if (!(player instanceof SimpleExoPlayer)) return;
|
||||
if (v == volumeOffButton) { // click to vol Off --> unmute
|
||||
volumeInfo.setTo(false, volumeInfo.getVolume());
|
||||
} else if (v == volumeUpButton) { // click to vol Up --> mute
|
||||
volumeInfo.setTo(true, volumeInfo.getVolume());
|
||||
}
|
||||
ToroExo.setVolumeInfo((SimpleExoPlayer) player, volumeInfo);
|
||||
updateVolumeButtons();
|
||||
}
|
||||
|
||||
/// TimeBar.OnScrubListener
|
||||
|
||||
@Override public void onScrubStart(TimeBar timeBar, long position) {
|
||||
dispatchOnScrubStart();
|
||||
}
|
||||
|
||||
@Override public void onScrubMove(TimeBar timeBar, long position) {
|
||||
dispatchOnScrubMove(position);
|
||||
}
|
||||
|
||||
@Override public void onScrubStop(TimeBar timeBar, long position, boolean canceled) {
|
||||
dispatchOnScrubStop(position);
|
||||
}
|
||||
|
||||
/// ToroPlayer.OnVolumeChangeListener
|
||||
|
||||
@Override public void onVolumeChanged(@NonNull VolumeInfo volumeInfo) {
|
||||
ToroControlView.this.volumeInfo.setTo(volumeInfo.isMute(), volumeInfo.getVolume());
|
||||
updateVolumeButtons();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2018 Nam Nguyen, nam@ene.im
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="#CC000000"
|
||||
android:gravity="center_vertical"
|
||||
android:layoutDirection="ltr"
|
||||
android:orientation="horizontal"
|
||||
android:theme="@style/Theme.AppCompat"
|
||||
tools:ignore="UnusedAttribute"
|
||||
>
|
||||
|
||||
<!--<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@id/exo_play"
|
||||
tools:ignore="ContentDescription"
|
||||
style="@style/ToroMediaButton.Play"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@id/exo_pause"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:visibility="gone"
|
||||
style="@style/ToroMediaButton.Pause"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@id/exo_position"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textColor="#FFBEBEBE"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="1:25"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:text="/"
|
||||
android:textColor="#FFBEBEBE"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@id/exo_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textColor="#FFBEBEBE"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="5:25"
|
||||
/>
|
||||
|
||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||
android:id="@id/exo_progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/exo_volume_up"
|
||||
tools:ignore="ContentDescription"
|
||||
style="@style/ToroMediaButton.VolumeUp"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/exo_volume_off"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:visibility="gone"
|
||||
style="@style/ToroMediaButton.VolumeOff"
|
||||
/>-->
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@id/exo_play"
|
||||
tools:ignore="ContentDescription"
|
||||
style="@style/ToroMediaButton.Play"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@id/exo_pause"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:visibility="gone"
|
||||
style="@style/ToroMediaButton.Pause"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@id/exo_position"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textColor="#FFBEBEBE"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="1:25"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:text="/"
|
||||
android:textColor="#FFBEBEBE"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:ignore="HardcodedText"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@id/exo_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textColor="#FFBEBEBE"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="5:25"
|
||||
/>
|
||||
|
||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||
android:id="@id/exo_progress"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_weight="1"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/exo_volume_up"
|
||||
tools:ignore="ContentDescription"
|
||||
style="@style/ToroMediaButton.VolumeUp"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageButton
|
||||
android:id="@+id/exo_volume_off"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:visibility="gone"
|
||||
style="@style/ToroMediaButton.VolumeOff"
|
||||
/>
|
||||
|
||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||
android:id="@+id/volume_bar"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
/>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
@ -1,89 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (c) 2018 Nam Nguyen, nam@ene.im
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
>
|
||||
|
||||
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||
android:id="@id/exo_content_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
>
|
||||
|
||||
<!-- Video surface will be inserted as the first child of the content frame. -->
|
||||
|
||||
<View
|
||||
android:id="@id/exo_shutter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@id/exo_artwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="fitXY"
|
||||
/>
|
||||
|
||||
<com.google.android.exoplayer2.ui.SubtitleView
|
||||
android:id="@id/exo_subtitles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@id/exo_error_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:background="@color/exo_error_message_background_color"
|
||||
android:gravity="center"
|
||||
android:padding="16dp"
|
||||
/>
|
||||
|
||||
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@id/exo_ad_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@id/exo_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@id/exo_buffering"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
/>
|
||||
|
||||
<ml.docilealligator.infinityforreddit.videoautoplay.ui.ToroControlView
|
||||
android:id="@id/exo_controller"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:controller_layout_id="@layout/toro_exo_control_view" />
|
||||
|
||||
</merge>
|
@ -1297,13 +1297,6 @@
|
||||
|
||||
<string name="unexpected_intent_action">Unexpected intent action: <xliff:g id="action">%1$s</xliff:g></string>
|
||||
<string name="enable_random_adaptation">Enable random adaptation</string>
|
||||
<string name="error_drm_not_supported">Protected content not supported on API levels below 18</string>
|
||||
<string name="error_drm_unsupported_scheme">This device does not support the required DRM scheme</string>
|
||||
<string name="error_drm_unknown">An unknown DRM error occurred</string>
|
||||
<string name="error_no_decoder">This device does not provide a decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
|
||||
<string name="error_no_secure_decoder">This device does not provide a secure decoder for <xliff:g id="mime_type">%1$s</xliff:g></string>
|
||||
<string name="error_querying_decoders">Unable to query device decoders</string>
|
||||
<string name="error_instantiating_decoder">Unable to instantiate decoder <xliff:g id="decoder_name">%1$s</xliff:g></string>
|
||||
<string name="error_unsupported_video">Media includes video tracks, but none are playable by this device</string>
|
||||
<string name="error_unsupported_audio">Media includes audio tracks, but none are playable by this device</string>
|
||||
<string name="storage_permission_denied">Permission to access storage was denied</string>
|
||||
|
Loading…
Reference in New Issue
Block a user