Update ExoPlayer.

This commit is contained in:
Docile-Alligator 2022-09-09 00:41:35 +10:00
parent 84b5b1d95d
commit 7293b9e758
25 changed files with 1303 additions and 1936 deletions

View File

@ -72,17 +72,12 @@ dependencies {
implementation 'com.google.android.material:material:1.5.0-alpha05' implementation 'com.google.android.material:material:1.5.0-alpha05'
/** ExoPlayer **/ /** 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-core:$exoplayerVersion"
implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayerVersion" implementation "com.google.android.exoplayer:exoplayer-dash:$exoplayerVersion"
implementation "com.google.android.exoplayer:exoplayer-hls:$exoplayerVersion" implementation "com.google.android.exoplayer:exoplayer-hls:$exoplayerVersion"
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayerVersion" implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayerVersion"
implementation "com.google.android.exoplayer:exoplayer-smoothstreaming:$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 **/ /** Third-party **/

View File

@ -27,9 +27,9 @@ import androidx.core.content.ContextCompat;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestManager; import com.bumptech.glide.RequestManager;
import com.bumptech.glide.request.RequestOptions; 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.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
@ -189,7 +189,7 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF
private FlairBottomSheetFragment mFlairSelectionBottomSheetFragment; private FlairBottomSheetFragment mFlairSelectionBottomSheetFragment;
private Snackbar mPostingSnackbar; private Snackbar mPostingSnackbar;
private DataSource.Factory dataSourceFactory; private DataSource.Factory dataSourceFactory;
private SimpleExoPlayer player; private ExoPlayer player;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -216,7 +216,7 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF
mGlide = Glide.with(this); mGlide = Glide.with(this);
player = ExoPlayerFactory.newSimpleInstance(this); player = new ExoPlayer.Builder(this).build();
videoPlayerView.setPlayer(player); videoPlayerView.setPlayer(player);
dataSourceFactory = new DefaultDataSourceFactory(this, dataSourceFactory = new DefaultDataSourceFactory(this,
Util.getUserAgent(this, "Infinity")); Util.getUserAgent(this, "Infinity"));
@ -491,7 +491,7 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF
constraintLayout.setVisibility(View.GONE); constraintLayout.setVisibility(View.GONE);
selectAgainTextView.setVisibility(View.VISIBLE); selectAgainTextView.setVisibility(View.VISIBLE);
videoPlayerView.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); player.setPlayWhenReady(true);
wasPlaying = true; wasPlaying = true;
} }

View File

@ -7,6 +7,7 @@ import static androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES;
import android.Manifest; import android.Manifest;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
@ -24,6 +25,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.provider.Settings; import android.provider.Settings;
import android.text.Html; import android.text.Html;
import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.OrientationEventListener; import android.view.OrientationEventListener;
@ -45,29 +47,26 @@ import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import com.google.android.exoplayer2.C; 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.Format;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; 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.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.HlsMediaSource; 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.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.PlayerControlView;
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder; import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.MimeTypes; 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.bottomappbar.BottomAppBar;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import com.google.common.collect.ImmutableList;
import com.otaliastudios.zoom.ZoomEngine; import com.otaliastudios.zoom.ZoomEngine;
import com.otaliastudios.zoom.ZoomSurfaceView; import com.otaliastudios.zoom.ZoomSurfaceView;
@ -176,7 +175,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
public Typeface typeface; public Typeface typeface;
private Uri mVideoUri; private Uri mVideoUri;
private SimpleExoPlayer player; private ExoPlayer player;
private DefaultTrackSelector trackSelector; private DefaultTrackSelector trackSelector;
private DataSource.Factory dataSourceFactory; private DataSource.Factory dataSourceFactory;
@ -403,7 +402,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
String postTitle = intent.getStringExtra(EXTRA_POST_TITLE); String postTitle = intent.getStringExtra(EXTRA_POST_TITLE);
setSmallTitle(postTitle); setSmallTitle(postTitle);
playerControlView.setVisibilityListener(visibility -> { playerControlView.addVisibilityListener(visibility -> {
switch (visibility) { switch (visibility) {
case View.GONE: case View.GONE:
getWindow().getDecorView().setSystemUiVisibility( getWindow().getDecorView().setSystemUiVisibility(
@ -422,21 +421,20 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
} }
}); });
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); trackSelector = new DefaultTrackSelector(this);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
if (videoType == VIDEO_TYPE_NORMAL && isDataSavingMode && dataSavingModeDefaultResolution > 0) { if (videoType == VIDEO_TYPE_NORMAL && isDataSavingMode && dataSavingModeDefaultResolution > 0) {
trackSelector.setParameters( trackSelector.setParameters(
trackSelector.buildUponParameters() trackSelector.buildUponParameters()
.setMaxVideoSize(dataSavingModeDefaultResolution, dataSavingModeDefaultResolution)); .setMaxVideoSize(dataSavingModeDefaultResolution, dataSavingModeDefaultResolution));
} }
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector); player = new ExoPlayer.Builder(this).setTrackSelector(trackSelector).build();
playerControlView.setPlayer(player); playerControlView.setPlayer(player);
player.addVideoListener(new VideoListener() { player.addListener(new Player.Listener() {
@Override @Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { public void onVideoSizeChanged(VideoSize videoSize) {
zoomSurfaceView.setContentSize(width, height); zoomSurfaceView.setContentSize(videoSize.width, videoSize.height);
} }
}); });
zoomSurfaceView.addCallback(new ZoomSurfaceView.Callback() { zoomSurfaceView.addCallback(new ZoomSurfaceView.Callback() {
@ -506,9 +504,10 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
if (mVideoUri == null) { if (mVideoUri == null) {
loadStreamableVideo(shortCode, savedInstanceState); loadStreamableVideo(shortCode, savedInstanceState);
} else { } else {
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
preparePlayer(savedInstanceState); preparePlayer(savedInstanceState);
} }
} else if (videoType == VIDEO_TYPE_V_REDD_IT) { } 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); loadGfycatOrRedgifsVideo(redgifsRetrofit, gfycatId, false, savedInstanceState, false);
} }
} else { } else {
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
preparePlayer(savedInstanceState); preparePlayer(savedInstanceState);
} }
} else if (videoType == VIDEO_TYPE_DIRECT || videoType == VIDEO_TYPE_IMGUR) { } 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); videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl);
} }
// Produces DataSource instances through which media data is loaded. // Produces DataSource instances through which media data is loaded.
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
// Prepare the player with the source. // 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); preparePlayer(savedInstanceState);
} else { } else {
videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL); videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL);
@ -561,10 +562,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
id = intent.getStringExtra(EXTRA_ID); id = intent.getStringExtra(EXTRA_ID);
videoFileName = subredditName + "-" + id + ".mp4"; videoFileName = subredditName + "-" + id + ".mp4";
// Produces DataSource instances through which media data is loaded. // Produces DataSource instances through which media data is loaded.
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
// Prepare the player with the source. // 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); preparePlayer(savedInstanceState);
} }
} }
@ -615,25 +617,29 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
muteButton.setImageResource(R.drawable.ic_unmute_24dp); muteButton.setImageResource(R.drawable.ic_unmute_24dp);
} }
player.addListener(new Player.EventListener() { player.addListener(new Player.Listener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
if (videoType == VIDEO_TYPE_NORMAL) { if (videoType == VIDEO_TYPE_NORMAL) {
hdButton.setVisibility(View.VISIBLE); hdButton.setVisibility(View.VISIBLE);
hdButton.setOnClickListener(view -> { 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.setShowDisableOption(true);
builder.setAllowAdaptiveSelections(false); builder.setAllowAdaptiveSelections(false);
AlertDialog alertDialog = builder.build(); Dialog dialog = builder.build();
alertDialog.show(); dialog.show();
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor()); if (dialog instanceof AlertDialog) {
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor()); 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++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
muteButton.setVisibility(View.VISIBLE); muteButton.setVisibility(View.VISIBLE);
muteButton.setOnClickListener(view -> { muteButton.setOnClickListener(view -> {
@ -688,10 +694,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
mVideoUri = Uri.parse(webm); mVideoUri = Uri.parse(webm);
videoDownloadUrl = mp4; videoDownloadUrl = mp4;
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
preparePlayer(savedInstanceState); preparePlayer(savedInstanceState);
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
} }
@Override @Override
@ -717,10 +724,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
mVideoUri = Uri.parse(webm); mVideoUri = Uri.parse(webm);
videoDownloadUrl = mp4; videoDownloadUrl = mp4;
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
preparePlayer(savedInstanceState); preparePlayer(savedInstanceState);
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
} }
@Override @Override
@ -782,10 +790,11 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
videoType = VIDEO_TYPE_IMGUR; videoType = VIDEO_TYPE_IMGUR;
videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl); videoFileName = "imgur-" + FilenameUtils.getName(videoDownloadUrl);
// Produces DataSource instances through which media data is loaded. // Produces DataSource instances through which media data is loaded.
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
// Prepare the player with the source. // 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); preparePlayer(savedInstanceState);
} else { } else {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
@ -797,11 +806,12 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
videoFileName = subredditName + "-" + id + ".mp4"; videoFileName = subredditName + "-" + id + ".mp4";
// Produces DataSource instances through which media data is loaded. // Produces DataSource instances through which media data is loaded.
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultHttpDataSourceFactory(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
// Prepare the player with the source. // Prepare the player with the source.
preparePlayer(savedInstanceState); preparePlayer(savedInstanceState);
player.prepare(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); player.prepare();
player.setMediaSource(new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
} else { } else {
Toast.makeText(ViewVideoActivity.this, R.string.error_fetching_v_redd_it_video_cannot_get_video_url, Toast.LENGTH_LONG).show(); 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); progressBar.setVisibility(View.GONE);
videoDownloadUrl = streamableVideo.mp4 == null ? streamableVideo.mp4Mobile.url : streamableVideo.mp4.url; videoDownloadUrl = streamableVideo.mp4 == null ? streamableVideo.mp4Mobile.url : streamableVideo.mp4.url;
mVideoUri = Uri.parse(videoDownloadUrl); mVideoUri = Uri.parse(videoDownloadUrl);
dataSourceFactory = new CacheDataSourceFactory(mSimpleCache, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
new DefaultDataSourceFactory(ViewVideoActivity.this, APIUtils.getRedgifsUserAgent(ViewVideoActivity.this))); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(ViewVideoActivity.this)));
preparePlayer(savedInstanceState); preparePlayer(savedInstanceState);
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(mVideoUri)));
} }
@Override @Override

View File

@ -42,10 +42,12 @@ import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target; 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.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.common.collect.ImmutableList;
import com.libRG.CustomTextView; import com.libRG.CustomTextView;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -2755,10 +2757,11 @@ public class HistoryPostRecyclerViewAdapter extends PagingDataAdapter<Post, Recy
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator); helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
helper.addEventListener(new Playable.DefaultEventListener() { helper.addEventListener(new Playable.DefaultEventListener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
if (mFragment.getMasterMutingOption() != null) { if (mFragment.getMasterMutingOption() != null) {
volume = mFragment.getMasterMutingOption() ? 0f : 1f; volume = mFragment.getMasterMutingOption() ? 0f : 1f;
@ -4022,10 +4025,11 @@ public class HistoryPostRecyclerViewAdapter extends PagingDataAdapter<Post, Recy
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator); helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
helper.addEventListener(new Playable.DefaultEventListener() { helper.addEventListener(new Playable.DefaultEventListener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
if (mFragment.getMasterMutingOption() != null) { if (mFragment.getMasterMutingOption() != null) {
volume = mFragment.getMasterMutingOption() ? 0f : 1f; volume = mFragment.getMasterMutingOption() ? 0f : 1f;

View File

@ -40,10 +40,10 @@ import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target; import com.bumptech.glide.request.target.Target;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.Tracks;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.common.collect.ImmutableList;
import com.libRG.CustomTextView; import com.libRG.CustomTextView;
import org.commonmark.ext.gfm.tables.TableBlock; 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 = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
helper.addEventListener(new Playable.DefaultEventListener() { helper.addEventListener(new Playable.DefaultEventListener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
helper.setVolume(volume); helper.setVolume(volume);
muteButton.setVisibility(View.VISIBLE); muteButton.setVisibility(View.VISIBLE);

View File

@ -42,10 +42,12 @@ import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target; 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.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.common.collect.ImmutableList;
import com.libRG.CustomTextView; import com.libRG.CustomTextView;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -2860,10 +2862,11 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator); helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
helper.addEventListener(new Playable.DefaultEventListener() { helper.addEventListener(new Playable.DefaultEventListener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
if (mFragment.getMasterMutingOption() != null) { if (mFragment.getMasterMutingOption() != null) {
volume = mFragment.getMasterMutingOption() ? 0f : 1f; volume = mFragment.getMasterMutingOption() ? 0f : 1f;
@ -4175,10 +4178,11 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator); helper = new ExoPlayerViewHelper(this, mediaUri, null, mExoCreator);
helper.addEventListener(new Playable.DefaultEventListener() { helper.addEventListener(new Playable.DefaultEventListener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
if (mFragment.getMasterMutingOption() != null) { if (mFragment.getMasterMutingOption() != null) {
volume = mFragment.getMasterMutingOption() ? 0f : 1f; volume = mFragment.getMasterMutingOption() ? 0f : 1f;

View File

@ -4,8 +4,8 @@ import android.content.SharedPreferences;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import ml.docilealligator.infinityforreddit.videoautoplay.Config; import ml.docilealligator.infinityforreddit.videoautoplay.Config;
@ -22,8 +22,8 @@ public class LoopAvailableExoCreator extends DefaultExoCreator {
@NonNull @NonNull
@Override @Override
public SimpleExoPlayer createPlayer() { public ExoPlayer createPlayer() {
SimpleExoPlayer player = super.createPlayer(); ExoPlayer player = super.createPlayer();
if (sharedPreferences.getBoolean(SharedPreferencesUtils.LOOP_VIDEO, true)) { if (sharedPreferences.getBoolean(SharedPreferencesUtils.LOOP_VIDEO, true)) {
player.setRepeatMode(Player.REPEAT_MODE_ALL); player.setRepeatMode(Player.REPEAT_MODE_ALL);
} else { } else {

View File

@ -7,7 +7,6 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -26,22 +25,21 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; 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.PlaybackParameters;
import com.google.android.exoplayer2.Player; 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.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.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.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.util.Util; 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.android.material.bottomappbar.BottomAppBar;
import com.google.common.collect.ImmutableList;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -54,6 +52,7 @@ import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.activities.ViewImgurMediaActivity; import ml.docilealligator.infinityforreddit.activities.ViewImgurMediaActivity;
import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment; import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment;
import ml.docilealligator.infinityforreddit.services.DownloadMediaService; import ml.docilealligator.infinityforreddit.services.DownloadMediaService;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import ml.docilealligator.infinityforreddit.utils.Utils; import ml.docilealligator.infinityforreddit.utils.Utils;
@ -78,7 +77,7 @@ public class ViewImgurVideoFragment extends Fragment {
ImageView downloadImageView; ImageView downloadImageView;
private ViewImgurMediaActivity activity; private ViewImgurMediaActivity activity;
private ImgurMedia imgurMedia; private ImgurMedia imgurMedia;
private SimpleExoPlayer player; private ExoPlayer player;
private DataSource.Factory dataSourceFactory; private DataSource.Factory dataSourceFactory;
private boolean wasPlaying = false; private boolean wasPlaying = false;
private boolean isMute = false; private boolean isMute = false;
@ -87,6 +86,8 @@ public class ViewImgurVideoFragment extends Fragment {
@Inject @Inject
@Named("default") @Named("default")
SharedPreferences mSharedPreferences; SharedPreferences mSharedPreferences;
@Inject
SimpleCache mSimpleCache;
public ViewImgurVideoFragment() { public ViewImgurVideoFragment() {
// Required empty public constructor // Required empty public constructor
@ -143,13 +144,13 @@ public class ViewImgurVideoFragment extends Fragment {
} }
}); });
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); TrackSelector trackSelector = new DefaultTrackSelector(activity);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); player = new ExoPlayer.Builder(activity).setTrackSelector(trackSelector).build();
player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector);
videoPlayerView.setPlayer(player); videoPlayerView.setPlayer(player);
dataSourceFactory = new DefaultDataSourceFactory(activity, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
Util.getUserAgent(activity, "Infinity")); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(activity)));
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(imgurMedia.getLink()))); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(imgurMedia.getLink())));
if (savedInstanceState != null) { if (savedInstanceState != null) {
playbackSpeed = savedInstanceState.getInt(PLAYBACK_SPEED_STATE); playbackSpeed = savedInstanceState.getInt(PLAYBACK_SPEED_STATE);
@ -278,12 +279,13 @@ public class ViewImgurVideoFragment extends Fragment {
muteButton.setImageResource(R.drawable.ic_unmute_24dp); muteButton.setImageResource(R.drawable.ic_unmute_24dp);
} }
player.addListener(new Player.EventListener() { player.addListener(new Player.Listener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
muteButton.setVisibility(View.VISIBLE); muteButton.setVisibility(View.VISIBLE);
muteButton.setOnClickListener(view -> { muteButton.setOnClickListener(view -> {

View File

@ -1,10 +1,10 @@
package ml.docilealligator.infinityforreddit.fragments; package ml.docilealligator.infinityforreddit.fragments;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -23,21 +23,20 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.RequestOptions;
import com.google.android.exoplayer2.ExoPlaybackException; 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.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.BehindLiveWindowException;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.HlsMediaSource; 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.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.PlayerView;
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder; import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.util.Util; 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.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -58,6 +57,7 @@ import ml.docilealligator.infinityforreddit.activities.RPANActivity;
import ml.docilealligator.infinityforreddit.adapters.RPANCommentStreamRecyclerViewAdapter; import ml.docilealligator.infinityforreddit.adapters.RPANCommentStreamRecyclerViewAdapter;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed; import ml.docilealligator.infinityforreddit.customviews.LinearLayoutManagerBugFixed;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.JSONUtils; import ml.docilealligator.infinityforreddit.utils.JSONUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -105,9 +105,11 @@ public class ViewRPANBroadcastFragment extends Fragment {
@Inject @Inject
@Named("rpan") @Named("rpan")
OkHttpClient okHttpClient; OkHttpClient okHttpClient;
@Inject
SimpleCache mSimpleCache;
private RPANActivity mActivity; private RPANActivity mActivity;
private RPANBroadcast rpanBroadcast; private RPANBroadcast rpanBroadcast;
private SimpleExoPlayer player; private ExoPlayer player;
private DefaultTrackSelector trackSelector; private DefaultTrackSelector trackSelector;
private DataSource.Factory dataSourceFactory; private DataSource.Factory dataSourceFactory;
private Handler handler; private Handler handler;
@ -175,9 +177,8 @@ public class ViewRPANBroadcastFragment extends Fragment {
} }
}); });
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); trackSelector = new DefaultTrackSelector(mActivity);
trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); player = new ExoPlayer.Builder(mActivity).setTrackSelector(trackSelector).build();
player = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector);
playerView.setPlayer(player); playerView.setPlayer(player);
wasPlaying = true; wasPlaying = true;
@ -202,9 +203,10 @@ public class ViewRPANBroadcastFragment extends Fragment {
muteButton.setImageResource(R.drawable.ic_unmute_24dp); muteButton.setImageResource(R.drawable.ic_unmute_24dp);
} }
player.addListener(new Player.EventListener() { player.addListener(new Player.Listener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
if (isDataSavingMode) { if (isDataSavingMode) {
trackSelector.setParameters( trackSelector.setParameters(
@ -214,18 +216,19 @@ public class ViewRPANBroadcastFragment extends Fragment {
hdButton.setVisibility(View.VISIBLE); hdButton.setVisibility(View.VISIBLE);
hdButton.setOnClickListener(view -> { hdButton.setOnClickListener(view -> {
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(mActivity, TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(mActivity, getString(R.string.select_video_quality), player, 0);
getString(R.string.select_video_quality), trackSelector, 0);
builder.setShowDisableOption(true); builder.setShowDisableOption(true);
builder.setAllowAdaptiveSelections(false); builder.setAllowAdaptiveSelections(false);
AlertDialog alertDialog = builder.build(); Dialog dialog = builder.build();
alertDialog.show(); dialog.show();
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor()); if (dialog instanceof AlertDialog) {
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor()); ((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++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
muteButton.setVisibility(View.VISIBLE); muteButton.setVisibility(View.VISIBLE);
muteButton.setOnClickListener(view -> { muteButton.setOnClickListener(view -> {
@ -350,9 +353,11 @@ public class ViewRPANBroadcastFragment extends Fragment {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
if (dataSourceFactory == null) { 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. // 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)) { if (mSharedPreferences.getBoolean(SharedPreferencesUtils.LOOP_VIDEO, true)) {
player.setRepeatMode(Player.REPEAT_MODE_ALL); player.setRepeatMode(Player.REPEAT_MODE_ALL);
} else { } else {

View File

@ -7,7 +7,6 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -26,22 +25,21 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; 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.PlaybackParameters;
import com.google.android.exoplayer2.Player; 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.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.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.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.util.Util; 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.android.material.bottomappbar.BottomAppBar;
import com.google.common.collect.ImmutableList;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -54,6 +52,7 @@ import ml.docilealligator.infinityforreddit.activities.ViewRedditGalleryActivity
import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment; import ml.docilealligator.infinityforreddit.bottomsheetfragments.PlaybackSpeedBottomSheetFragment;
import ml.docilealligator.infinityforreddit.post.Post; import ml.docilealligator.infinityforreddit.post.Post;
import ml.docilealligator.infinityforreddit.services.DownloadMediaService; import ml.docilealligator.infinityforreddit.services.DownloadMediaService;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import ml.docilealligator.infinityforreddit.utils.Utils; import ml.docilealligator.infinityforreddit.utils.Utils;
@ -82,7 +81,7 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
private Post.Gallery galleryVideo; private Post.Gallery galleryVideo;
private String subredditName; private String subredditName;
private boolean isNsfw; private boolean isNsfw;
private SimpleExoPlayer player; private ExoPlayer player;
private DataSource.Factory dataSourceFactory; private DataSource.Factory dataSourceFactory;
private boolean wasPlaying = false; private boolean wasPlaying = false;
private boolean isMute = false; private boolean isMute = false;
@ -91,6 +90,8 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
@Inject @Inject
@Named("default") @Named("default")
SharedPreferences mSharedPreferences; SharedPreferences mSharedPreferences;
@Inject
SimpleCache mSimpleCache;
public ViewRedditGalleryVideoFragment() { public ViewRedditGalleryVideoFragment() {
// Required empty public constructor // Required empty public constructor
@ -153,13 +154,13 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
} }
}); });
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(); TrackSelector trackSelector = new DefaultTrackSelector(activity);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); player = new ExoPlayer.Builder(activity).setTrackSelector(trackSelector).build();
player = ExoPlayerFactory.newSimpleInstance(activity, trackSelector);
videoPlayerView.setPlayer(player); videoPlayerView.setPlayer(player);
dataSourceFactory = new DefaultDataSourceFactory(activity, dataSourceFactory = new CacheDataSource.Factory().setCache(mSimpleCache)
Util.getUserAgent(activity, "Infinity")); .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory().setUserAgent(APIUtils.getRedgifsUserAgent(activity)));
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(galleryVideo.url))); player.prepare();
player.setMediaSource(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(galleryVideo.url)));
if (savedInstanceState != null) { if (savedInstanceState != null) {
playbackSpeed = savedInstanceState.getInt(PLAYBACK_SPEED_STATE); playbackSpeed = savedInstanceState.getInt(PLAYBACK_SPEED_STATE);
@ -290,12 +291,13 @@ public class ViewRedditGalleryVideoFragment extends Fragment {
muteButton.setImageResource(R.drawable.ic_unmute_24dp); muteButton.setImageResource(R.drawable.ic_unmute_24dp);
} }
player.addListener(new Player.EventListener() { player.addListener(new Player.Listener() {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) { if (!trackGroups.isEmpty()) {
for (int i = 0; i < trackGroups.length; i++) { for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getFormat(0).sampleMimeType; String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) { if (mimeType != null && mimeType.contains("audio")) {
muteButton.setVisibility(View.VISIBLE); muteButton.setVisibility(View.VISIBLE);
muteButton.setOnClickListener(view -> { muteButton.setOnClickListener(view -> {

View File

@ -29,15 +29,11 @@ import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory.ExtensionRendererMode; import com.google.android.exoplayer2.DefaultRenderersFactory.ExtensionRendererMode;
import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.SimpleExoPlayer; 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.source.MediaSource;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.cache.Cache; 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 * Necessary configuration for {@link ExoCreator} to produces {@link SimpleExoPlayer} and
* {@link MediaSource}. Instance of this class must be construct using {@link Builder}. * {@link MediaSource}. Instance of this class must be construct using {@link Builder}.
@ -61,7 +57,6 @@ public final class Config {
@NonNull final MediaSourceBuilder mediaSourceBuilder; @NonNull final MediaSourceBuilder mediaSourceBuilder;
// Nullable options // Nullable options
@Nullable final DrmSessionManager<FrameworkMediaCrypto> drmSessionManager;
@Nullable final Cache cache; // null by default @Nullable final Cache cache; // null by default
// If null, ExoCreator must come up with a default one. // If null, ExoCreator must come up with a default one.
// This is to help customizing the Data source, for example using OkHttp extension. // This is to help customizing the Data source, for example using OkHttp extension.
@ -72,14 +67,13 @@ public final class Config {
@NonNull LoadControl loadControl, @NonNull LoadControl loadControl,
@Nullable DataSource.Factory dataSourceFactory, @Nullable DataSource.Factory dataSourceFactory,
@NonNull MediaSourceBuilder mediaSourceBuilder, @NonNull MediaSourceBuilder mediaSourceBuilder,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, @Nullable Cache cache) { @Nullable Cache cache) {
this.context = context != null ? context.getApplicationContext() : null; this.context = context != null ? context.getApplicationContext() : null;
this.extensionMode = extensionMode; this.extensionMode = extensionMode;
this.meter = meter; this.meter = meter;
this.loadControl = loadControl; this.loadControl = loadControl;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.mediaSourceBuilder = mediaSourceBuilder; this.mediaSourceBuilder = mediaSourceBuilder;
this.drmSessionManager = drmSessionManager;
this.cache = cache; this.cache = cache;
} }
@ -93,7 +87,6 @@ public final class Config {
if (!meter.equals(config.meter)) return false; if (!meter.equals(config.meter)) return false;
if (!loadControl.equals(config.loadControl)) return false; if (!loadControl.equals(config.loadControl)) return false;
if (!mediaSourceBuilder.equals(config.mediaSourceBuilder)) 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; if (!ObjectsCompat.equals(cache, config.cache)) return false;
return ObjectsCompat.equals(dataSourceFactory, config.dataSourceFactory); return ObjectsCompat.equals(dataSourceFactory, config.dataSourceFactory);
} }
@ -103,7 +96,6 @@ public final class Config {
result = 31 * result + meter.hashCode(); result = 31 * result + meter.hashCode();
result = 31 * result + loadControl.hashCode(); result = 31 * result + loadControl.hashCode();
result = 31 * result + mediaSourceBuilder.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 + (cache != null ? cache.hashCode() : 0);
result = 31 * result + (dataSourceFactory != null ? dataSourceFactory.hashCode() : 0); result = 31 * result + (dataSourceFactory != null ? dataSourceFactory.hashCode() : 0);
return result; return result;
@ -111,7 +103,6 @@ public final class Config {
@SuppressWarnings("unused") public Builder newBuilder() { @SuppressWarnings("unused") public Builder newBuilder() {
return new Builder(context).setCache(this.cache) return new Builder(context).setCache(this.cache)
.setDrmSessionManager(this.drmSessionManager)
.setExtensionMode(this.extensionMode) .setExtensionMode(this.extensionMode)
.setLoadControl(this.loadControl) .setLoadControl(this.loadControl)
.setMediaSourceBuilder(this.mediaSourceBuilder) .setMediaSourceBuilder(this.mediaSourceBuilder)
@ -145,7 +136,6 @@ public final class Config {
private LoadControl loadControl = new DefaultLoadControl(); private LoadControl loadControl = new DefaultLoadControl();
private DataSource.Factory dataSourceFactory = null; private DataSource.Factory dataSourceFactory = null;
private MediaSourceBuilder mediaSourceBuilder = MediaSourceBuilder.DEFAULT; private MediaSourceBuilder mediaSourceBuilder = MediaSourceBuilder.DEFAULT;
private DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = null;
private Cache cache = null; private Cache cache = null;
public Builder setExtensionMode(@ExtensionRendererMode int extensionMode) { public Builder setExtensionMode(@ExtensionRendererMode int extensionMode) {
@ -175,13 +165,6 @@ public final class Config {
return this; return this;
} }
@Beta //
public Builder setDrmSessionManager(
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager) {
this.drmSessionManager = drmSessionManager;
return this;
}
public Builder setCache(@Nullable Cache cache) { public Builder setCache(@Nullable Cache cache) {
this.cache = cache; this.cache = cache;
return this; return this;
@ -189,7 +172,7 @@ public final class Config {
public Config build() { public Config build() {
return new Config(context, extensionMode, meter, loadControl, dataSourceFactory, return new Config(context, extensionMode, meter, loadControl, dataSourceFactory,
mediaSourceBuilder, drmSessionManager, cache); mediaSourceBuilder, cache);
} }
} }
} }

View File

@ -27,22 +27,26 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.LoadControl;
import com.google.android.exoplayer2.RenderersFactory; 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.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory; import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import ml.docilealligator.infinityforreddit.utils.APIUtils;
/** /**
* Usage: use this as-it or inheritance. * Usage: use this as-it or inheritance.
* *
@ -50,7 +54,7 @@ import java.io.IOException;
* @since 3.4.0 * @since 3.4.0
*/ */
@SuppressWarnings({ "unused", "WeakerAccess" }) // @SuppressWarnings({"unused", "WeakerAccess"}) //
public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener { public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
final ToroExo toro; // per application final ToroExo toro; // per application
@ -75,13 +79,13 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
DataSource.Factory baseFactory = config.dataSourceFactory; DataSource.Factory baseFactory = config.dataSourceFactory;
if (baseFactory == null) { 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, // DataSource.Factory factory = new DefaultDataSource.Factory(this.toro.context, baseFactory);
config.meter, baseFactory); if (config.cache != null)
if (config.cache != null) factory = new CacheDataSourceFactory(config.cache, factory); factory = new CacheDataSource.Factory().setCache(config.cache).setUpstreamDataSourceFactory(baseFactory);
mediaDataSourceFactory = factory; mediaDataSourceFactory = factory;
manifestDataSourceFactory = new DefaultDataSourceFactory(this.toro.context, this.toro.appName); manifestDataSourceFactory = new DefaultDataSource.Factory(this.toro.context);
} }
public DefaultExoCreator(Context context, Config config) { public DefaultExoCreator(Context context, Config config) {
@ -89,7 +93,8 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
} }
@SuppressWarnings("SimplifiableIfStatement") @SuppressWarnings("SimplifiableIfStatement")
@Override public boolean equals(Object o) { @Override
public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
@ -104,7 +109,8 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
return manifestDataSourceFactory.equals(that.manifestDataSourceFactory); return manifestDataSourceFactory.equals(that.manifestDataSourceFactory);
} }
@Override public int hashCode() { @Override
public int hashCode() {
int result = toro.hashCode(); int result = toro.hashCode();
result = 31 * result + trackSelector.hashCode(); result = 31 * result + trackSelector.hashCode();
result = 31 * result + loadControl.hashCode(); result = 31 * result + loadControl.hashCode();
@ -119,22 +125,28 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
return trackSelector; return trackSelector;
} }
@Nullable @Override public Context getContext() { @Nullable
@Override
public Context getContext() {
return toro.context; return toro.context;
} }
@NonNull @Override public SimpleExoPlayer createPlayer() { @NonNull
@Override
public ExoPlayer createPlayer() {
return new ToroExoPlayer(toro.context, renderersFactory, trackSelector, loadControl, return new ToroExoPlayer(toro.context, renderersFactory, trackSelector, loadControl,
new DefaultBandwidthMeter.Builder(toro.context).build(), config.drmSessionManager, new DefaultBandwidthMeter.Builder(toro.context).build(), Util.getCurrentOrMainLooper()).getPlayer();
Util.getLooper());
} }
@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(), return mediaSourceBuilder.buildMediaSource(this.toro.context, uri, fileExt, new Handler(),
manifestDataSourceFactory, mediaDataSourceFactory, this); manifestDataSourceFactory, mediaDataSourceFactory, this);
} }
@NonNull @Override // @NonNull
@Override //
public Playable createPlayable(@NonNull Uri uri, String fileExt) { public Playable createPlayable(@NonNull Uri uri, String fileExt) {
return new PlayableImpl(this, uri, fileExt); return new PlayableImpl(this, uri, fileExt);
} }
@ -166,28 +178,15 @@ public class DefaultExoCreator implements ExoCreator, MediaSourceEventListener {
// no-ops // no-ops
} }
@Override public void onReadingStarted(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
// no-ops
}
@Override @Override
public void onUpstreamDiscarded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId, public void onUpstreamDiscarded(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId,
MediaLoadData mediaLoadData) { MediaLoadData mediaLoadData) {
// no-ops // no-ops
} }
@Override public void onDownstreamFormatChanged(int windowIndex, @Override
public void onDownstreamFormatChanged(int windowIndex,
@Nullable MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) { @Nullable MediaSource.MediaPeriodId mediaPeriodId, MediaLoadData mediaLoadData) {
// no-ops // no-ops
} }
@Override
public void onMediaPeriodCreated(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
// no-ops
}
@Override
public void onMediaPeriodReleased(int windowIndex, MediaSource.MediaPeriodId mediaPeriodId) {
// no-ops
}
} }

View File

@ -22,6 +22,7 @@ import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
@ -52,7 +53,8 @@ public interface ExoCreator {
* *
* @return a new {@link SimpleExoPlayer} instance. * @return a new {@link SimpleExoPlayer} instance.
*/ */
@NonNull SimpleExoPlayer createPlayer(); @NonNull
ExoPlayer createPlayer();
/** /**
* Create a {@link MediaSource} from media {@link Uri}. * Create a {@link MediaSource} from media {@link Uri}.

View File

@ -17,7 +17,6 @@
package ml.docilealligator.infinityforreddit.videoautoplay; package ml.docilealligator.infinityforreddit.videoautoplay;
import static com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS; import static com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo.RENDERER_SUPPORT_UNSUPPORTED_TRACKS;
import static ml.docilealligator.infinityforreddit.videoautoplay.ToroExo.toro; import static ml.docilealligator.infinityforreddit.videoautoplay.ToroExo.toro;
import android.net.Uri; import android.net.Uri;
@ -28,16 +27,16 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; 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.SimpleExoPlayer;
import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.Tracks;
import com.google.android.exoplayer2.mediacodec.MediaCodecUtil;
import com.google.android.exoplayer2.source.BehindLiveWindowException; 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.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; 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.trackselection.TrackSelector;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
import com.google.common.collect.ImmutableList;
import ml.docilealligator.infinityforreddit.R; import ml.docilealligator.infinityforreddit.R;
@ -52,13 +51,14 @@ import ml.docilealligator.infinityforreddit.R;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public class ExoPlayable extends PlayableImpl { 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; private EventListener listener;
// Adapt from ExoPlayer demo. // Adapt from ExoPlayer demo.
protected boolean inErrorState = false; 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 * 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); super(creator, uri, fileExt);
} }
@Override public void prepare(boolean prepareSource) { @Override
public void prepare(boolean prepareSource) {
if (listener == null) { if (listener == null) {
listener = new Listener(); listener = new Listener();
super.addEventListener(listener); super.addEventListener(listener);
@ -83,7 +84,8 @@ public class ExoPlayable extends PlayableImpl {
this.inErrorState = false; this.inErrorState = false;
} }
@Override public void setPlayerView(@Nullable PlayerView playerView) { @Override
public void setPlayerView(@Nullable PlayerView playerView) {
// This will also clear these flags // This will also clear these flags
if (playerView != this.playerView) { if (playerView != this.playerView) {
this.lastSeenTrackGroupArray = null; this.lastSeenTrackGroupArray = null;
@ -92,13 +94,15 @@ public class ExoPlayable extends PlayableImpl {
super.setPlayerView(playerView); super.setPlayerView(playerView);
} }
@Override public void reset() { @Override
public void reset() {
super.reset(); super.reset();
this.lastSeenTrackGroupArray = null; this.lastSeenTrackGroupArray = null;
this.inErrorState = false; this.inErrorState = false;
} }
@Override public void release() { @Override
public void release() {
if (listener != null) { if (listener != null) {
super.removeEventListener(listener); super.removeEventListener(listener);
listener = null; listener = null;
@ -108,7 +112,7 @@ public class ExoPlayable extends PlayableImpl {
this.inErrorState = false; this.inErrorState = false;
} }
@SuppressWarnings({ "unused" }) // @SuppressWarnings({"unused"}) //
protected void onErrorMessage(@NonNull String message) { protected void onErrorMessage(@NonNull String message) {
// Sub class can have custom reaction about the error here, including not to show this toast // Sub class can have custom reaction about the error here, including not to show this toast
// (by not calling super.onErrorMessage(message)). // (by not calling super.onErrorMessage(message)).
@ -120,10 +124,9 @@ public class ExoPlayable extends PlayableImpl {
} }
class Listener extends DefaultEventListener { class Listener extends DefaultEventListener {
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
super.onTracksChanged(trackGroups, trackSelections); ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (trackGroups == lastSeenTrackGroupArray) return; if (trackGroups == lastSeenTrackGroupArray) return;
lastSeenTrackGroupArray = trackGroups; lastSeenTrackGroupArray = trackGroups;
if (!(creator instanceof DefaultExoCreator)) return; if (!(creator instanceof DefaultExoCreator)) return;
@ -142,34 +145,8 @@ public class ExoPlayable extends PlayableImpl {
} }
} }
@Override public void onPlayerError(ExoPlaybackException error) { @Override
/// Adapt from ExoPlayer Demo public void onPlayerError(@NonNull PlaybackException error) {
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);
inErrorState = true; inErrorState = true;
if (isBehindLiveWindow(error)) { if (isBehindLiveWindow(error)) {
ExoPlayable.super.reset(); ExoPlayable.super.reset();
@ -180,7 +157,8 @@ public class ExoPlayable extends PlayableImpl {
super.onPlayerError(error); 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) { if (inErrorState) {
// Adapt from ExoPlayer demo. // Adapt from ExoPlayer demo.
// "This will only occur if the user has performed a seek whilst in the error state. Update // "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 // position to which they seek." - ExoPlayer
ExoPlayable.super.updatePlaybackInfo(); ExoPlayable.super.updatePlaybackInfo();
} }
super.onPositionDiscontinuity(oldPosition, newPosition, reason);
super.onPositionDiscontinuity(reason);
} }
} }
static boolean isBehindLiveWindow(ExoPlaybackException error) { static boolean isBehindLiveWindow(PlaybackException error) {
if (error.type != ExoPlaybackException.TYPE_SOURCE) return false; if (error instanceof ExoPlaybackException && ((ExoPlaybackException) error).type != ExoPlaybackException.TYPE_SOURCE) return false;
Throwable cause = error.getSourceException(); Throwable cause = error.getCause();
while (cause != null) { while (cause != null) {
if (cause instanceof BehindLiveWindowException) return true; if (cause instanceof BehindLiveWindowException) return true;
cause = cause.getCause(); cause = cause.getCause();

View File

@ -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;
}
}
}

View File

@ -28,6 +28,7 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.ContentType; 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.LoopingMediaSource;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener;
@ -46,14 +47,16 @@ import com.google.android.exoplayer2.upstream.DataSource;
public interface MediaSourceBuilder { 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, @Nullable String fileExt, @Nullable Handler handler,
@NonNull DataSource.Factory manifestDataSourceFactory, @NonNull DataSource.Factory manifestDataSourceFactory,
@NonNull DataSource.Factory mediaDataSourceFactory, @NonNull DataSource.Factory mediaDataSourceFactory,
@Nullable MediaSourceEventListener listener); @Nullable MediaSourceEventListener listener);
MediaSourceBuilder DEFAULT = new MediaSourceBuilder() { MediaSourceBuilder DEFAULT = new MediaSourceBuilder() {
@NonNull @Override @NonNull
@Override
public MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri, public MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri,
@Nullable String ext, @Nullable Handler handler, @Nullable String ext, @Nullable Handler handler,
@NonNull DataSource.Factory manifestDataSourceFactory, @NonNull DataSource.Factory manifestDataSourceFactory,
@ -61,23 +64,23 @@ public interface MediaSourceBuilder {
@ContentType int type = isEmpty(ext) ? inferContentType(uri) : inferContentType("." + ext); @ContentType int type = isEmpty(ext) ? inferContentType(uri) : inferContentType("." + ext);
MediaSource result; MediaSource result;
switch (type) { switch (type) {
case C.TYPE_SS: case C.CONTENT_TYPE_SS:
result = new SsMediaSource.Factory( result = new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory), manifestDataSourceFactory) new DefaultSsChunkSource.Factory(mediaDataSourceFactory), manifestDataSourceFactory)
.createMediaSource(uri); .createMediaSource(MediaItem.fromUri(uri));
break; break;
case C.TYPE_DASH: case C.CONTENT_TYPE_DASH:
result = new DashMediaSource.Factory( result = new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory), manifestDataSourceFactory) new DefaultDashChunkSource.Factory(mediaDataSourceFactory), manifestDataSourceFactory)
.createMediaSource(uri); .createMediaSource(MediaItem.fromUri(uri));
break; break;
case C.TYPE_HLS: case C.CONTENT_TYPE_HLS:
result = new HlsMediaSource.Factory(mediaDataSourceFactory) // result = new HlsMediaSource.Factory(mediaDataSourceFactory) //
.createMediaSource(uri); .createMediaSource(MediaItem.fromUri(uri));
break; break;
case C.TYPE_OTHER: case C.CONTENT_TYPE_OTHER:
result = new ProgressiveMediaSource.Factory(mediaDataSourceFactory) // result = new ProgressiveMediaSource.Factory(mediaDataSourceFactory) //
.createMediaSource(uri); .createMediaSource(MediaItem.fromUri(uri));
break; break;
default: default:
throw new IllegalStateException("Unsupported type: " + type); throw new IllegalStateException("Unsupported type: " + type);
@ -90,7 +93,8 @@ public interface MediaSourceBuilder {
MediaSourceBuilder LOOPING = new MediaSourceBuilder() { MediaSourceBuilder LOOPING = new MediaSourceBuilder() {
@NonNull @Override @NonNull
@Override
public MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri, public MediaSource buildMediaSource(@NonNull Context context, @NonNull Uri uri,
@Nullable String fileExt, @Nullable Handler handler, @Nullable String fileExt, @Nullable Handler handler,
@NonNull DataSource.Factory manifestDataSourceFactory, @NonNull DataSource.Factory manifestDataSourceFactory,

View File

@ -20,20 +20,20 @@ import androidx.annotation.FloatRange;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; 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.Metadata;
import com.google.android.exoplayer2.metadata.MetadataOutput; import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.MediaSource; 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.Cue;
import com.google.android.exoplayer2.text.CueGroup;
import com.google.android.exoplayer2.text.TextOutput; 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.ui.PlayerView;
import com.google.android.exoplayer2.video.VideoListener; import com.google.android.exoplayer2.video.VideoSize;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet; 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}. * 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 * 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 * strong reference to Activity, and if it supports any kind of that, make sure to implicitly clean
* it up. * it up.
@ -62,7 +62,7 @@ public interface Playable {
* - Configure {@link EventListener} for it. * - Configure {@link EventListener} for it.
* - If there is non-trivial PlaybackInfo, update it to the SimpleExoPlayer. * - If there is non-trivial PlaybackInfo, update it to the SimpleExoPlayer.
* - If client request to prepare MediaSource, then prepare it. * - If client request to prepare MediaSource, then prepare it.
* * <p>
* This method must be called before {@link #setPlayerView(PlayerView)}. * This method must be called before {@link #setPlayerView(PlayerView)}.
* *
* @param prepareSource if {@code true}, also prepare the MediaSource when preparing the Player, * @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 * 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, * 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()}. * that is after {@link #prepare(boolean)} and before {@link #release()}.
* * <p>
* Changing the PlayerView during playback is expected, though not always recommended, especially * Changing the PlayerView during playback is expected, though not always recommended, especially
* on old Devices with low Android API. * on old Devices with low Android API.
* *
@ -87,7 +87,8 @@ public interface Playable {
* *
* @return current PlayerView instance of this 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. * 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. * @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. * @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); void setVolume(@FloatRange(from = 0.0, to = 1.0) float volume);
/** /**
@ -172,8 +175,10 @@ public interface Playable {
* @return current volume value. * @return current volume value.
* @deprecated use {@link #getVolumeInfo()} instead. * @deprecated use {@link #getVolumeInfo()} instead.
*/ */
@RemoveIn(version = "3.6.0") @Deprecated // @RemoveIn(version = "3.6.0")
@FloatRange(from = 0.0, to = 1.0) float getVolume(); @Deprecated //
@FloatRange(from = 0.0, to = 1.0)
float getVolume();
/** /**
* Update playback's volume. * Update playback's volume.
@ -186,7 +191,8 @@ public interface Playable {
/** /**
* Get current {@link VolumeInfo}. * Get current {@link VolumeInfo}.
*/ */
@NonNull VolumeInfo getVolumeInfo(); @NonNull
VolumeInfo getVolumeInfo();
/** /**
* Same as {@link Player#setPlaybackParameters(PlaybackParameters)} * Same as {@link Player#setPlaybackParameters(PlaybackParameters)}
@ -196,167 +202,201 @@ public interface Playable {
/** /**
* Same as {@link Player#getPlaybackParameters()} * Same as {@link Player#getPlaybackParameters()}
*/ */
@Nullable PlaybackParameters getParameters(); @Nullable
PlaybackParameters getParameters();
void addErrorListener(@NonNull ToroPlayer.OnErrorListener listener); void addErrorListener(@NonNull ToroPlayer.OnErrorListener listener);
void removeErrorListener(@Nullable ToroPlayer.OnErrorListener listener); void removeErrorListener(@Nullable ToroPlayer.OnErrorListener listener);
// Combine necessary interfaces. // Combine necessary interfaces.
interface EventListener extends Player.EventListener, VideoListener, TextOutput, MetadataOutput { interface EventListener extends Player.Listener, TextOutput, MetadataOutput {
} @Override
default void onCues(@NonNull List<Cue> cues) {
/** Default empty implementation */
class DefaultEventListener implements EventListener {
@Override public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { default void onCues(@NonNull CueGroup cueGroup) {
} }
@Override public void onLoadingChanged(boolean isLoading) { @Override
default void onMetadata(@NonNull Metadata metadata) {
}
@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) {
} }
} }
/** 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 { class EventListeners extends CopyOnWriteArraySet<EventListener> implements EventListener {
EventListeners() { EventListeners() {
} }
@Override public void onVideoSizeChanged(int width, int height, int unAppliedRotationDegrees, @Override
float pixelWidthHeightRatio) { public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
for (EventListener eventListener : this) { for (EventListener eventListener : this) {
eventListener.onVideoSizeChanged(width, height, unAppliedRotationDegrees, eventListener.onVideoSizeChanged(videoSize);
pixelWidthHeightRatio);
} }
} }
@Override public void onRenderedFirstFrame() { @Override
public void onRenderedFirstFrame() {
for (EventListener eventListener : this) { for (EventListener eventListener : this) {
eventListener.onRenderedFirstFrame(); 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) { for (EventListener eventListener : this) {
eventListener.onTimelineChanged(timeline, manifest, reason); eventListener.onTimelineChanged(timeline, reason);
} }
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { public void onTracksChanged(@NonNull Tracks tracks) {
for (EventListener eventListener : this) { 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) { 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) { 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) { for (EventListener eventListener : this) {
eventListener.onRepeatModeChanged(repeatMode); eventListener.onRepeatModeChanged(repeatMode);
} }
} }
@Override public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) { @Override
public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
for (EventListener eventListener : this) { for (EventListener eventListener : this) {
eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled); eventListener.onShuffleModeEnabledChanged(shuffleModeEnabled);
} }
} }
@Override public void onPlayerError(ExoPlaybackException error) { @Override
public void onPlayerError(@NonNull PlaybackException error) {
for (EventListener eventListener : this) { for (EventListener eventListener : this) {
eventListener.onPlayerError(error); 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) { 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) { for (EventListener eventListener : this) {
eventListener.onPlaybackParametersChanged(playbackParameters); eventListener.onPlaybackParametersChanged(playbackParameters);
} }
} }
@Override public void onSeekProcessed() { @Override
public void onCues(@NonNull CueGroup cueGroup) {
for (EventListener eventListener : this) { for (EventListener eventListener : this) {
eventListener.onSeekProcessed(); eventListener.onCues(cueGroup);
} }
} }
@Override public void onCues(List<Cue> cues) { @Override
for (EventListener eventListener : this) { public void onMetadata(@NonNull Metadata metadata) {
eventListener.onCues(cues);
}
}
@Override public void onMetadata(Metadata metadata) {
for (EventListener eventListener : this) { for (EventListener eventListener : this) {
eventListener.onMetadata(metadata); eventListener.onMetadata(metadata);
} }

View File

@ -30,7 +30,6 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.ui.PlayerView; import com.google.android.exoplayer2.ui.PlayerView;
@ -39,9 +38,9 @@ import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
/** /**
* [20180225] * [20180225]
* * <p>
* Default implementation of {@link Playable}. * Default implementation of {@link Playable}.
* * <p>
* Instance of {@link Playable} should be reusable. Retaining instance of Playable across config * 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. * 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 String fileExt;
protected final ExoCreator creator; // required, cached 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 MediaSource mediaSource; // on-demand, since we do not reuse MediaSource now.
protected PlayerView playerView; // on-demand, not always required. protected PlayerView playerView; // on-demand, not always required.
@ -73,84 +72,101 @@ class PlayableImpl implements Playable {
this.fileExt = fileExt; this.fileExt = fileExt;
} }
@CallSuper @Override public void prepare(boolean prepareSource) { @CallSuper
@Override
public void prepare(boolean prepareSource) {
if (prepareSource) { if (prepareSource) {
ensureMediaSource(); ensureMediaSource();
ensurePlayerView(); ensurePlayerView();
} }
} }
@CallSuper @Override public void setPlayerView(@Nullable PlayerView playerView) { @CallSuper
@Override
public void setPlayerView(@Nullable PlayerView playerView) {
if (this.playerView == playerView) return; if (this.playerView == playerView) return;
if (playerView == null) { if (playerView == null) {
this.playerView.setPlayer(null); this.playerView.setPlayer(null);
} else { } else {
if (this.player != null) { if (this.player != null) {
PlayerView.switchTargetView(this.player, this.playerView, playerView); PlayerView.switchTargetView(this.player.getPlayer(), this.playerView, playerView);
} }
} }
this.playerView = playerView; this.playerView = playerView;
} }
@Override public final PlayerView getPlayerView() { @Override
public final PlayerView getPlayerView() {
return this.playerView; return this.playerView;
} }
@CallSuper @Override public void play() { @CallSuper
@Override
public void play() {
ensureMediaSource(); ensureMediaSource();
ensurePlayerView(); ensurePlayerView();
checkNotNull(player, "Playable#play(): Player is null!"); 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. // 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(); this.playbackInfo.reset();
if (player != null) { if (player != null) {
// reset volume to default // reset volume to default
ToroExo.setVolumeInfo(this.player, new VolumeInfo(false, 1.f)); 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.mediaSource = null; // so it will be re-prepared when play() is called.
this.sourcePrepared = false; this.sourcePrepared = false;
} }
@CallSuper @Override public void release() { @CallSuper
@Override
public void release() {
this.setPlayerView(null); this.setPlayerView(null);
if (this.player != null) { if (this.player != null) {
// reset volume to default // reset volume to default
ToroExo.setVolumeInfo(this.player, new VolumeInfo(false, 1.f)); ToroExo.setVolumeInfo(this.player, new VolumeInfo(false, 1.f));
this.player.stop(true); player.getPlayer().stop();
player.getPlayer().clearMediaItems();
if (listenerApplied) { if (listenerApplied) {
player.removeListener(listeners); player.getPlayer().removeListener(listeners);
player.removeVideoListener(listeners); if (this.player != null) {
player.removeTextOutput(listeners); this.player.removeOnVolumeChangeListener(this.volumeChangeListeners);
player.removeMetadataOutput(listeners);
if (this.player instanceof ToroExoPlayer) {
((ToroExoPlayer) this.player).removeOnVolumeChangeListener(this.volumeChangeListeners);
} }
listenerApplied = false; listenerApplied = false;
} }
with(checkNotNull(creator.getContext(), "ExoCreator has no Context")) // with(checkNotNull(creator.getContext(), "ExoCreator has no Context")) //
.releasePlayer(this.creator, this.player); .releasePlayer(this.creator, this.player.getPlayer());
} }
this.player = null; this.player = null;
this.mediaSource = null; this.mediaSource = null;
this.sourcePrepared = false; this.sourcePrepared = false;
} }
@CallSuper @NonNull @Override public PlaybackInfo getPlaybackInfo() { @CallSuper
@NonNull
@Override
public PlaybackInfo getPlaybackInfo() {
updatePlaybackInfo(); updatePlaybackInfo();
return new PlaybackInfo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition(), return new PlaybackInfo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition(),
playbackInfo.getVolumeInfo()); 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.setResumeWindow(playbackInfo.getResumeWindow());
this.playbackInfo.setResumePosition(playbackInfo.getResumePosition()); this.playbackInfo.setResumePosition(playbackInfo.getResumePosition());
this.setVolumeInfo(playbackInfo.getVolumeInfo()); this.setVolumeInfo(playbackInfo.getVolumeInfo());
@ -159,31 +175,38 @@ class PlayableImpl implements Playable {
ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo()); ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo());
boolean haveResumePosition = this.playbackInfo.getResumeWindow() != INDEX_UNSET; boolean haveResumePosition = this.playbackInfo.getResumeWindow() != INDEX_UNSET;
if (haveResumePosition) { 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 //noinspection ConstantConditions
if (listener != null) this.listeners.add(listener); 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); 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!"); checkNotNull(player, "Playable#setVolume(): Player is null!");
playbackInfo.getVolumeInfo().setTo(volume == 0, volume); playbackInfo.getVolumeInfo().setTo(volume == 0, volume);
ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo()); ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo());
} }
@CallSuper @Override public float getVolume() { @CallSuper
return checkNotNull(player, "Playable#getVolume(): Player is null!").getVolume(); @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)); boolean changed = !this.playbackInfo.getVolumeInfo().equals(checkNotNull(volumeInfo));
if (changed) { if (changed) {
this.playbackInfo.getVolumeInfo().setTo(volumeInfo.isMute(), volumeInfo.getVolume()); this.playbackInfo.getVolumeInfo().setTo(volumeInfo.isMute(), volumeInfo.getVolume());
@ -192,17 +215,21 @@ class PlayableImpl implements Playable {
return changed; return changed;
} }
@NonNull @Override public VolumeInfo getVolumeInfo() { @NonNull
@Override
public VolumeInfo getVolumeInfo() {
return this.playbackInfo.getVolumeInfo(); return this.playbackInfo.getVolumeInfo();
} }
@Override public void setParameters(@Nullable PlaybackParameters parameters) { @Override
checkNotNull(player, "Playable#setParameters(PlaybackParameters): Player is null") // public void setParameters(@Nullable PlaybackParameters parameters) {
checkNotNull(player.getPlayer(), "Playable#setParameters(PlaybackParameters): Player is null") //
.setPlaybackParameters(parameters); .setPlaybackParameters(parameters);
} }
@Override public PlaybackParameters getParameters() { @Override
return checkNotNull(player, "Playable#getParameters(): Player is null").getPlaybackParameters(); public PlaybackParameters getParameters() {
return checkNotNull(player.getPlayer(), "Playable#getParameters(): Player is null").getPlaybackParameters();
} }
@Override @Override
@ -215,28 +242,31 @@ class PlayableImpl implements Playable {
volumeChangeListeners.remove(listener); volumeChangeListeners.remove(listener);
} }
@Override public boolean isPlaying() { @Override
return player != null && player.getPlayWhenReady(); 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)); 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); this.errorListeners.remove(listener);
} }
final void updatePlaybackInfo() { final void updatePlaybackInfo() {
if (player == null || player.getPlaybackState() == Player.STATE_IDLE) return; if (player == null || player.getPlayer().getPlaybackState() == Player.STATE_IDLE) return;
playbackInfo.setResumeWindow(player.getCurrentWindowIndex()); playbackInfo.setResumeWindow(player.getPlayer().getCurrentWindowIndex());
playbackInfo.setResumePosition(player.isCurrentWindowSeekable() ? // playbackInfo.setResumePosition(player.getPlayer().isCurrentWindowSeekable() ? //
Math.max(0, player.getCurrentPosition()) : TIME_UNSET); Math.max(0, player.getPlayer().getCurrentPosition()) : TIME_UNSET);
playbackInfo.setVolumeInfo(ToroExo.getVolumeInfo(player)); playbackInfo.setVolumeInfo(ToroExo.getVolumeInfo(player));
} }
private void ensurePlayerView() { 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. // TODO [20180822] Double check this.
@ -249,7 +279,7 @@ class PlayableImpl implements Playable {
if (!sourcePrepared) { if (!sourcePrepared) {
ensurePlayer(); // sourcePrepared is set to false only when player is null. ensurePlayer(); // sourcePrepared is set to false only when player is null.
beforePrepareMediaSource(); beforePrepareMediaSource();
player.prepare(mediaSource, playbackInfo.getResumeWindow() == C.INDEX_UNSET, false); player.getPlayer().prepare(mediaSource, playbackInfo.getResumeWindow() == C.INDEX_UNSET, false);
sourcePrepared = true; sourcePrepared = true;
} }
} }
@ -263,20 +293,15 @@ class PlayableImpl implements Playable {
} }
if (!listenerApplied) { if (!listenerApplied) {
if (player instanceof ToroExoPlayer) { player.addOnVolumeChangeListener(volumeChangeListeners);
((ToroExoPlayer) player).addOnVolumeChangeListener(volumeChangeListeners); player.getPlayer().addListener(listeners);
}
player.addListener(listeners);
player.addVideoListener(listeners);
player.addTextOutput(listeners);
player.addMetadataOutput(listeners);
listenerApplied = true; listenerApplied = true;
} }
ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo()); ToroExo.setVolumeInfo(player, this.playbackInfo.getVolumeInfo());
boolean haveResumePosition = playbackInfo.getResumeWindow() != C.INDEX_UNSET; boolean haveResumePosition = playbackInfo.getResumeWindow() != C.INDEX_UNSET;
if (haveResumePosition) { if (haveResumePosition) {
player.seekTo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition()); player.getPlayer().seekTo(playbackInfo.getResumeWindow(), playbackInfo.getResumePosition());
} }
} }

View File

@ -16,34 +16,21 @@
package ml.docilealligator.infinityforreddit.videoautoplay; 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 java.lang.Runtime.getRuntime;
import static ml.docilealligator.infinityforreddit.videoautoplay.ToroUtil.checkNotNull; import static ml.docilealligator.infinityforreddit.videoautoplay.ToroUtil.checkNotNull;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.text.TextUtils;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo; import androidx.annotation.RestrictTo;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import androidx.core.util.Pools; import androidx.core.util.Pools;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.SimpleExoPlayer; 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 com.google.android.exoplayer2.util.Util;
import java.net.CookieHandler; import java.net.CookieHandler;
@ -52,18 +39,15 @@ import java.net.CookiePolicy;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.videoautoplay.media.DrmMedia;
import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo; import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
/** /**
* Global helper class to manage {@link ExoCreator} and {@link SimpleExoPlayer} instances. * Global helper class to manage {@link ExoCreator} and {@link SimpleExoPlayer} instances.
* In this setup, {@link ExoCreator} and SimpleExoPlayer pools are cached. A {@link Config} * In this setup, {@link ExoCreator} and SimpleExoPlayer pools are cached. A {@link Config}
* is a key for each {@link ExoCreator}. * is a key for each {@link ExoCreator}.
* * <p>
* A suggested usage is as below: * A suggested usage is as below:
* <pre><code> * <pre><code>
* ExoCreator creator = ToroExo.with(this).getDefaultCreator(); * ExoCreator creator = ToroExo.with(this).getDefaultCreator();
@ -95,10 +79,14 @@ public final class ToroExo {
return toro; return toro;
} }
@NonNull final String appName; @NonNull
@NonNull final Context context; // Application context final String appName;
@NonNull private final Map<Config, ExoCreator> creators; @NonNull
@NonNull private final Map<ExoCreator, Pools.Pool<SimpleExoPlayer>> playerPools; 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. private Config defaultConfig; // will be created on the first time it is used.
@ -129,7 +117,8 @@ public final class ToroExo {
return creator; return creator;
} }
@SuppressWarnings("WeakerAccess") public final Config getDefaultConfig() { @SuppressWarnings("WeakerAccess")
public final Config getDefaultConfig() {
if (defaultConfig == null) defaultConfig = new Config.Builder(context).build(); if (defaultConfig == null) defaultConfig = new Config.Builder(context).build();
return defaultConfig; 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 * Request an instance of {@link SimpleExoPlayer}. It can be an existing instance cached by Pool
* or new one. * or new one.
* * <p>
* The creator may or may not be the one created by either {@link #getCreator(Config)} or * The creator may or may not be the one created by either {@link #getCreator(Config)} or
* {@link #getDefaultCreator()}. * {@link #getDefaultCreator()}.
* *
@ -152,10 +141,10 @@ public final class ToroExo {
* @return an usable {@link SimpleExoPlayer} instance. * @return an usable {@link SimpleExoPlayer} instance.
*/ */
@NonNull // @NonNull //
public final SimpleExoPlayer requestPlayer(@NonNull ExoCreator creator) { public final ToroExoPlayer requestPlayer(@NonNull ExoCreator creator) {
SimpleExoPlayer player = getPool(checkNotNull(creator)).acquire(); ExoPlayer player = getPool(checkNotNull(creator)).acquire();
if (player == null) player = creator.createPlayer(); if (player == null) player = creator.createPlayer();
return player; return new ToroExoPlayer(player);
} }
/** /**
@ -165,8 +154,8 @@ public final class ToroExo {
* @param player the {@link SimpleExoPlayer} to be released back to the Pool * @param player the {@link SimpleExoPlayer} to be released back to the Pool
* @return true if player is released to relevant Pool, false otherwise. * @return true if player is released to relevant Pool, false otherwise.
*/ */
@SuppressWarnings({ "WeakerAccess", "UnusedReturnValue" }) // @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); return getPool(checkNotNull(creator)).release(player);
} }
@ -176,18 +165,18 @@ public final class ToroExo {
*/ */
public final void cleanUp() { public final void cleanUp() {
// TODO [2018/03/07] Test this. Ref: https://stackoverflow.com/a/1884916/1553254 // 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(); ) { playerPools.entrySet().iterator(); it.hasNext(); ) {
Pools.Pool<SimpleExoPlayer> pool = it.next().getValue(); Pools.Pool<ExoPlayer> pool = it.next().getValue();
SimpleExoPlayer item; ExoPlayer item;
while ((item = pool.acquire()) != null) item.release(); while ((item = pool.acquire()) != null) item.release();
it.remove(); it.remove();
} }
} }
/// internal APIs /// internal APIs
private Pools.Pool<SimpleExoPlayer> getPool(ExoCreator creator) { private Pools.Pool<ExoPlayer> getPool(ExoCreator creator) {
Pools.Pool<SimpleExoPlayer> pool = playerPools.get(creator); Pools.Pool<ExoPlayer> pool = playerPools.get(creator);
if (pool == null) { if (pool == null) {
pool = new Pools.SimplePool<>(MAX_POOL_SIZE); pool = new Pools.SimplePool<>(MAX_POOL_SIZE);
playerPools.put(creator, pool); playerPools.put(creator, pool);
@ -204,90 +193,17 @@ public final class ToroExo {
this.context.getString(resId) : this.context.getString(resId, params); 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. // Share the code of setting Volume. For use inside library only.
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) //
public static void setVolumeInfo(@NonNull SimpleExoPlayer player, public static void setVolumeInfo(@NonNull ToroExoPlayer player,
@NonNull VolumeInfo volumeInfo) { @NonNull VolumeInfo volumeInfo) {
if (player instanceof ToroExoPlayer) { player.setVolumeInfo(volumeInfo);
((ToroExoPlayer) player).setVolumeInfo(volumeInfo);
} else {
if (volumeInfo.isMute()) {
player.setVolume(0f);
} else {
player.setVolume(volumeInfo.getVolume());
}
}
} }
@SuppressWarnings("WeakerAccess") @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) // @SuppressWarnings("WeakerAccess")
public static VolumeInfo getVolumeInfo(SimpleExoPlayer player) { @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) //
if (player instanceof ToroExoPlayer) { public static VolumeInfo getVolumeInfo(ToroExoPlayer player) {
return new VolumeInfo(((ToroExoPlayer) player).getVolumeInfo()); return new VolumeInfo(player.getVolumeInfo());
} else {
float volume = player.getVolume();
return new VolumeInfo(volume == 0, volume);
}
} }
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")

View File

@ -23,13 +23,11 @@ import android.os.Looper;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.NonNull; 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.LoadControl;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer; 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.trackselection.TrackSelector;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
@ -41,13 +39,18 @@ import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
* @author eneim (2018/03/27). * @author eneim (2018/03/27).
*/ */
@SuppressWarnings("WeakerAccess") // @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, TrackSelector trackSelector, LoadControl loadControl, BandwidthMeter bandwidthMeter,
@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager, Looper looper) { Looper looper) {
super(context, renderersFactory, trackSelector, loadControl, bandwidthMeter, drmSessionManager, player = new ExoPlayer.Builder(context).setRenderersFactory(renderersFactory).setTrackSelector(trackSelector).setLoadControl(loadControl).setBandwidthMeter(bandwidthMeter).setLooper(looper).build();
looper); }
public ToroExoPlayer(ExoPlayer exoPlayer) {
this.player = exoPlayer;
} }
private ToroPlayer.VolumeChangeListeners listeners; private ToroPlayer.VolumeChangeListeners listeners;
@ -65,7 +68,8 @@ public class ToroExoPlayer extends SimpleExoPlayer {
if (this.listeners != null) this.listeners.clear(); 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)); this.setVolumeInfo(new VolumeInfo(audioVolume == 0, audioVolume));
} }
@ -76,7 +80,7 @@ public class ToroExoPlayer extends SimpleExoPlayer {
boolean changed = !this.volumeInfo.equals(volumeInfo); boolean changed = !this.volumeInfo.equals(volumeInfo);
if (changed) { if (changed) {
this.volumeInfo.setTo(volumeInfo.isMute(), volumeInfo.getVolume()); this.volumeInfo.setTo(volumeInfo.isMute(), volumeInfo.getVolume());
super.setVolume(volumeInfo.isMute() ? 0 : volumeInfo.getVolume()); player.setVolume(volumeInfo.isMute() ? 0 : volumeInfo.getVolume());
if (listeners != null) { if (listeners != null) {
for (ToroPlayer.OnVolumeChangeListener listener : this.listeners) { for (ToroPlayer.OnVolumeChangeListener listener : this.listeners) {
listener.onVolumeChanged(volumeInfo); listener.onVolumeChanged(volumeInfo);
@ -87,7 +91,13 @@ public class ToroExoPlayer extends SimpleExoPlayer {
return changed; return changed;
} }
@SuppressWarnings("unused") @NonNull public final VolumeInfo getVolumeInfo() { @SuppressWarnings("unused")
@NonNull
public final VolumeInfo getVolumeInfo() {
return volumeInfo; return volumeInfo;
} }
public ExoPlayer getPlayer() {
return player;
}
} }

View File

@ -34,7 +34,8 @@ import ml.docilealligator.infinityforreddit.videoautoplay.widget.Container;
public final class ToroUtil { public final class ToroUtil {
@SuppressWarnings("unused") private static final String TAG = "ToroLib:Util"; @SuppressWarnings("unused")
private static final String TAG = "ToroLib:Util";
private ToroUtil() { private ToroUtil() {
throw new RuntimeException("Meh!"); throw new RuntimeException("Meh!");
@ -76,7 +77,8 @@ public final class ToroUtil {
* @return the non-null reference that was validated * @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null * @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) { if (reference == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
@ -93,7 +95,8 @@ public final class ToroUtil {
* @return the non-null reference that was validated * @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null * @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) { if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage)); throw new NullPointerException(String.valueOf(errorMessage));
} }

View File

@ -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();
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -1297,13 +1297,6 @@
<string name="unexpected_intent_action">Unexpected intent action: <xliff:g id="action">%1$s</xliff:g></string> <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="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_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="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> <string name="storage_permission_denied">Permission to access storage was denied</string>