Respect Reddit Video Default Resolution for autoplaying videos in PostRecyclerViewAdapter.

This commit is contained in:
Docile-Alligator
2025-08-29 16:46:56 -04:00
parent ae1d87926b
commit 232d97ed90
8 changed files with 291 additions and 142 deletions

View File

@ -177,7 +177,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
private boolean isDataSavingMode;
private int dataSavingModeDefaultResolution;
private int nonDataSavingModeDefaultResolution;
private boolean setNonDataSavingModeDefaultResolutionAlready = false;
private boolean setDefaultResolutionAlready = false;
private Integer originalOrientation;
private int playbackSpeed = 100;
private boolean useBottomAppBar;
@ -520,7 +520,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
videoType = savedInstanceState.getInt(VIDEO_TYPE_STATE);
subredditName = savedInstanceState.getString(SUBREDDIT_NAME_STATE);
id = savedInstanceState.getString(ID_STATE);
setNonDataSavingModeDefaultResolutionAlready = savedInstanceState.getBoolean(SET_NON_DATA_SAVING_MODE_DEFAULT_RESOLUTION_ALREADY_STATE);
setDefaultResolutionAlready = savedInstanceState.getBoolean(SET_NON_DATA_SAVING_MODE_DEFAULT_RESOLUTION_ALREADY_STATE);
setPlaybackSpeed(savedInstanceState.getInt(PLAYBACK_SPEED_STATE, 100));
}
@ -546,8 +546,8 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) {
if (videoType == VIDEO_TYPE_NORMAL) {
binding.getHdButton().setVisibility(View.VISIBLE);
binding.getHdButton().setOnClickListener(view -> {
binding.getVideoQualityButton().setVisibility(View.VISIBLE);
binding.getVideoQualityButton().setOnClickListener(view -> {
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(ViewVideoActivity.this, getString(R.string.select_video_quality), player, C.TRACK_TYPE_VIDEO);
builder.setShowDisableOption(true);
builder.setAllowAdaptiveSelections(false);
@ -559,7 +559,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
}
});
if (!setNonDataSavingModeDefaultResolutionAlready) {
if (!setDefaultResolutionAlready) {
int desiredResolution = 0;
if (isDataSavingMode) {
if (dataSavingModeDefaultResolution > 0) {
@ -616,7 +616,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
);
}
}
setNonDataSavingModeDefaultResolutionAlready = true;
setDefaultResolutionAlready = true;
}
}
@ -1051,7 +1051,7 @@ public class ViewVideoActivity extends AppCompatActivity implements CustomFontRe
outState.putString(ID_STATE, id);
}
outState.putInt(PLAYBACK_SPEED_STATE, playbackSpeed);
outState.putBoolean(SET_NON_DATA_SAVING_MODE_DEFAULT_RESOLUTION_ALREADY_STATE, setNonDataSavingModeDefaultResolutionAlready);
outState.putBoolean(SET_NON_DATA_SAVING_MODE_DEFAULT_RESOLUTION_ALREADY_STATE, setDefaultResolutionAlready);
}
@Override

View File

@ -25,7 +25,7 @@ public class ViewVideoActivityBindingAdapter {
private final MaterialButton forwardButton;
private final MaterialButton rewindButton;
private final MaterialButton muteButton;
private final MaterialButton hdButton;
private final MaterialButton videoQualityButton;
private final BottomAppBar bottomAppBar;
private final TextView titleTextView;
private final MaterialButton backButton;
@ -38,7 +38,7 @@ public class ViewVideoActivityBindingAdapter {
forwardButton = binding.getRoot().findViewById(R.id.exo_ffwd);
rewindButton = binding.getRoot().findViewById(R.id.exo_rew);
muteButton = binding.getRoot().findViewById(R.id.mute_exo_playback_control_view);
hdButton = binding.getRoot().findViewById(R.id.hd_exo_playback_control_view);
videoQualityButton = binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view);
bottomAppBar = binding.getRoot().findViewById(R.id.bottom_navigation_exo_playback_control_view);
titleTextView = binding.getRoot().findViewById(R.id.title_text_view_exo_playback_control_view);
backButton = binding.getRoot().findViewById(R.id.back_button_exo_playback_control_view);
@ -52,7 +52,7 @@ public class ViewVideoActivityBindingAdapter {
forwardButton = binding.getRoot().findViewById(R.id.exo_ffwd);
rewindButton = binding.getRoot().findViewById(R.id.exo_rew);
muteButton = binding.getRoot().findViewById(R.id.mute_exo_playback_control_view);
hdButton = binding.getRoot().findViewById(R.id.hd_exo_playback_control_view);
videoQualityButton = binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view);
bottomAppBar = binding.getRoot().findViewById(R.id.bottom_navigation_exo_playback_control_view);
titleTextView = binding.getRoot().findViewById(R.id.title_text_view_exo_playback_control_view);
backButton = binding.getRoot().findViewById(R.id.back_button_exo_playback_control_view);
@ -88,8 +88,8 @@ public class ViewVideoActivityBindingAdapter {
return muteButton;
}
public MaterialButton getHdButton() {
return hdButton;
public MaterialButton getVideoQualityButton() {
return videoQualityButton;
}
public BottomAppBar getBottomAppBar() {

View File

@ -1,5 +1,6 @@
package ml.docilealligator.infinityforreddit.adapters;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
@ -27,12 +28,15 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.core.content.ContextCompat;
import androidx.media3.common.C;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.Tracks;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
@ -40,6 +44,7 @@ import androidx.media3.ui.AspectRatioFrameLayout;
import androidx.media3.ui.DefaultTimeBar;
import androidx.media3.ui.PlayerView;
import androidx.media3.ui.TimeBar;
import androidx.media3.ui.TrackSelectionDialogBuilder;
import androidx.paging.PagingDataAdapter;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ItemTouchHelper;
@ -256,6 +261,8 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
private boolean mFixedHeightPreviewInCard;
private boolean mHideTextPostContent;
private boolean mEasierToWatchInFullScreen;
private int mDataSavingModeDefaultResolution;
private int mNonDataSavingModeDefaultResolution;
private String mLongPressPostNonMediaAreaAction = SharedPreferencesUtils.LONG_PRESS_POST_VALUE_SHOW_POST_OPTIONS;
private String mLongPressPostMediaAction = SharedPreferencesUtils.LONG_PRESS_POST_VALUE_SHOW_POST_OPTIONS;
private boolean mHandleReadPost;
@ -342,6 +349,8 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
mFixedHeightPreviewInCard = sharedPreferences.getBoolean(SharedPreferencesUtils.FIXED_HEIGHT_PREVIEW_IN_CARD, false);
mHideTextPostContent = sharedPreferences.getBoolean(SharedPreferencesUtils.HIDE_TEXT_POST_CONTENT, false);
mEasierToWatchInFullScreen = sharedPreferences.getBoolean(SharedPreferencesUtils.EASIER_TO_WATCH_IN_FULL_SCREEN, false);
mDataSavingModeDefaultResolution = Integer.parseInt(mSharedPreferences.getString(SharedPreferencesUtils.REDDIT_VIDEO_DEFAULT_RESOLUTION, "360"));
mNonDataSavingModeDefaultResolution = Integer.parseInt(mSharedPreferences.getString(SharedPreferencesUtils.REDDIT_VIDEO_DEFAULT_RESOLUTION_NO_DATA_SAVING, "0"));
mPostLayout = postLayout;
mDefaultLinkPostLayout = Integer.parseInt(sharedPreferences.getString(SharedPreferencesUtils.DEFAULT_LINK_POST_LAYOUT_KEY, "-1"));
@ -1632,12 +1641,14 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.fetchRedgifsOrStreamableVideoCall = null;
}
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.errorLoadingRedgifsImageView.setVisibility(View.GONE);
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.videoQualityButton.setVisibility(View.GONE);
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.muteButton.setVisibility(View.GONE);
if (!((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.isManuallyPaused) {
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.resetVolume();
}
mGlide.clear(((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.previewImageView);
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.previewImageView.setVisibility(View.GONE);
((PostBaseVideoAutoplayViewHolder) holder).toroPlayer.setDefaultResolutionAlready = false;
} else if (holder instanceof PostWithPreviewTypeViewHolder) {
mGlide.clear(((PostWithPreviewTypeViewHolder) holder).imageView);
if (((PostWithPreviewTypeViewHolder) holder).imageWrapperFrameLayout != null) {
@ -2512,6 +2523,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
GifImageView previewImageView;
ImageView errorLoadingRedgifsImageView;
PlayerView videoPlayer;
ImageView videoQualityButton;
ImageView muteButton;
ImageView fullscreenButton;
ImageView playPauseButton;
@ -2525,10 +2537,11 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
private boolean isManuallyPaused;
private Drawable playDrawable;
private Drawable pauseDrawable;
private boolean setDefaultResolutionAlready;
public VideoAutoplayImpl(View itemView, AspectRatioFrameLayout aspectRatioFrameLayout,
GifImageView previewImageView, ImageView errorLoadingRedgifsImageView,
PlayerView videoPlayer, ImageView muteButton, ImageView fullscreenButton,
PlayerView videoPlayer, ImageView videoQualityButton, ImageView muteButton, ImageView fullscreenButton,
ImageView playPauseButton, DefaultTimeBar progressBar,
Drawable playDrawable, Drawable pauseDrawable) {
this.itemView = itemView;
@ -2536,6 +2549,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
this.previewImageView = previewImageView;
this.errorLoadingRedgifsImageView = errorLoadingRedgifsImageView;
this.videoPlayer = videoPlayer;
this.videoQualityButton = videoQualityButton;
this.muteButton = muteButton;
this.fullscreenButton = fullscreenButton;
this.playPauseButton = playPauseButton;
@ -2736,6 +2750,81 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
public void onTracksChanged(@NonNull Tracks tracks) {
ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();
if (!trackGroups.isEmpty()) {
if (getPost().isNormalVideo()) {
videoQualityButton.setVisibility(View.VISIBLE);
videoQualityButton.setOnClickListener(view -> {
TrackSelectionDialogBuilder builder = new TrackSelectionDialogBuilder(mActivity, mActivity.getString(R.string.select_video_quality), helper.getPlayer(), C.TRACK_TYPE_VIDEO);
builder.setShowDisableOption(true);
builder.setAllowAdaptiveSelections(false);
Dialog dialog = builder.setTheme(R.style.MaterialAlertDialogTheme).build();
dialog.show();
if (dialog instanceof AlertDialog) {
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
((AlertDialog) dialog).getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
}
});
if (!setDefaultResolutionAlready) {
int desiredResolution = 0;
if (mDataSavingMode) {
if (mDataSavingModeDefaultResolution > 0) {
desiredResolution = mDataSavingModeDefaultResolution;
}
} else if (mNonDataSavingModeDefaultResolution > 0) {
desiredResolution = mNonDataSavingModeDefaultResolution;
}
if (desiredResolution > 0) {
TrackSelectionOverride trackSelectionOverride = null;
int bestTrackIndex = -1;
int bestResolution = -1;
int worstResolution = Integer.MAX_VALUE;
int worstTrackIndex = -1;
Tracks.Group bestTrackGroup = null;
Tracks.Group worstTrackGroup = null;
for (Tracks.Group trackGroup : tracks.getGroups()) {
if (trackGroup.getType() == C.TRACK_TYPE_VIDEO) {
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
int trackResolution = Math.min(trackGroup.getTrackFormat(trackIndex).height, trackGroup.getTrackFormat(trackIndex).width);
if (trackResolution <= desiredResolution && trackResolution > bestResolution) {
bestTrackIndex = trackIndex;
bestResolution = trackResolution;
bestTrackGroup = trackGroup;
}
if (trackResolution < worstResolution) {
worstTrackIndex = trackIndex;
worstResolution = trackResolution;
worstTrackGroup = trackGroup;
}
}
}
}
if (bestTrackIndex != -1 && bestTrackGroup != null) {
trackSelectionOverride = new TrackSelectionOverride(
bestTrackGroup.getMediaTrackGroup(),
ImmutableList.of(bestTrackIndex)
);
} else if (worstTrackIndex != -1 && worstTrackGroup != null) {
trackSelectionOverride = new TrackSelectionOverride(
worstTrackGroup.getMediaTrackGroup(),
ImmutableList.of(worstTrackIndex)
);
}
if (trackSelectionOverride != null) {
helper.getPlayer().setTrackSelectionParameters(
helper.getPlayer().getTrackSelectionParameters()
.buildUpon()
.addOverride(trackSelectionOverride)
.build()
);
}
}
setDefaultResolutionAlready = true;
}
}
for (int i = 0; i < trackGroups.size(); i++) {
String mimeType = trackGroups.get(i).getTrackFormat(0).sampleMimeType;
if (mimeType != null && mimeType.contains("audio")) {
@ -3043,6 +3132,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
GifImageView previewImageView,
ImageView errorLoadingRedgifsImageView,
PlayerView videoPlayer,
ImageView videoQualityButton,
ImageView muteButton,
ImageView fullscreenButton,
ImageView playPauseButton,
@ -3078,7 +3168,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
shareButton);
toroPlayer = new VideoAutoplayImpl(rootView, aspectRatioFrameLayout, previewImageView,
errorLoadingRedgifsImageView, videoPlayer, muteButton, fullscreenButton, playPauseButton,
errorLoadingRedgifsImageView, videoPlayer, videoQualityButton, muteButton, fullscreenButton, playPauseButton,
progressBar,
AppCompatResources.getDrawable(mActivity, R.drawable.ic_play_arrow_24dp),
AppCompatResources.getDrawable(mActivity, R.drawable.ic_pause_24dp)) {
@ -3175,6 +3265,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
binding.previewImageViewItemPostVideoTypeAutoplay,
binding.errorLoadingVideoImageViewItemPostVideoTypeAutoplay,
binding.playerViewItemPostVideoTypeAutoplay,
binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view),
binding.getRoot().findViewById(R.id.mute_exo_playback_control_view),
binding.getRoot().findViewById(R.id.fullscreen_exo_playback_control_view),
binding.getRoot().findViewById(R.id.exo_play),
@ -3215,6 +3306,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
binding.previewImageViewItemPostVideoTypeAutoplay,
binding.errorLoadingVideoImageViewItemPostVideoTypeAutoplay,
binding.playerViewItemPostVideoTypeAutoplay,
binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view),
binding.getRoot().findViewById(R.id.mute_exo_playback_control_view),
binding.getRoot().findViewById(R.id.fullscreen_exo_playback_control_view),
binding.getRoot().findViewById(R.id.exo_play),
@ -4515,6 +4607,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
binding.previewImageViewItemPostCard2VideoAutoplay,
binding.errorLoadingVideoImageViewItemPostCard2VideoAutoplay,
binding.playerViewItemPostCard2VideoAutoplay,
binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view),
binding.getRoot().findViewById(R.id.mute_exo_playback_control_view),
binding.getRoot().findViewById(R.id.fullscreen_exo_playback_control_view),
binding.getRoot().findViewById(R.id.exo_play),
@ -4557,6 +4650,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
binding.previewImageViewItemPostCard2VideoAutoplay,
binding.errorLoadingVideoImageViewItemPostCard2VideoAutoplay,
binding.playerViewItemPostCard2VideoAutoplay,
binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view),
binding.getRoot().findViewById(R.id.mute_exo_playback_control_view),
binding.getRoot().findViewById(R.id.fullscreen_exo_playback_control_view),
binding.getRoot().findViewById(R.id.exo_play),
@ -4715,6 +4809,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
binding.previewImageViewItemPostCard3VideoTypeAutoplay,
binding.errorLoadingVideoImageViewItemPostCard3VideoTypeAutoplay,
binding.playerViewItemPostCard3VideoTypeAutoplay,
binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view),
binding.getRoot().findViewById(R.id.mute_exo_playback_control_view),
binding.getRoot().findViewById(R.id.fullscreen_exo_playback_control_view),
binding.getRoot().findViewById(R.id.exo_play),
@ -4755,6 +4850,7 @@ public class PostRecyclerViewAdapter extends PagingDataAdapter<Post, RecyclerVie
binding.previewImageViewItemPostCard3VideoTypeAutoplay,
binding.errorLoadingVideoImageViewItemPostCard3VideoTypeAutoplay,
binding.playerViewItemPostCard3VideoTypeAutoplay,
binding.getRoot().findViewById(R.id.video_quality_exo_playback_control_view),
binding.getRoot().findViewById(R.id.mute_exo_playback_control_view),
binding.getRoot().findViewById(R.id.fullscreen_exo_playback_control_view),
binding.getRoot().findViewById(R.id.exo_play),

View File

@ -406,6 +406,10 @@ public class Post implements Parcelable {
this.isStreamable = isStreamable;
}
public boolean isNormalVideo() {
return postType == Post.VIDEO_TYPE && !isImgur && !isRedgifs && !isStreamable;
}
public boolean isLoadRedgifsOrStreamableVideoSuccess() {
return loadRedgifsOrStreamableVideoSuccess;
}

View File

@ -44,138 +44,163 @@ import ml.docilealligator.infinityforreddit.videoautoplay.widget.Container;
public class ExoPlayerViewHelper extends ToroPlayerHelper {
@NonNull private final ExoPlayable playable;
@UnstableApi
@NonNull private final MyEventListeners listeners;
private final boolean lazyPrepare;
@NonNull
private final ExoPlayable playable;
@UnstableApi
@NonNull
private final MyEventListeners listeners;
private final boolean lazyPrepare;
// Container is no longer required for constructing new instance.
@SuppressWarnings("unused") @RemoveIn(version = "3.6.0") @Deprecated //
public ExoPlayerViewHelper(Container container, @NonNull ToroPlayer player, @NonNull Uri uri) {
this(player, uri);
}
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri) {
this(player, uri, null);
}
@OptIn(markerClass = UnstableApi.class)
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri,
@Nullable String fileExt) {
this(player, uri, fileExt, with(player.getPlayerView().getContext()).getDefaultCreator());
}
/** Config instance should be kept as global instance. */
@OptIn(markerClass = UnstableApi.class)
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
@NonNull Config config) {
this(player, uri, fileExt,
with(player.getPlayerView().getContext()).getCreator(checkNotNull(config)));
}
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
@NonNull ExoCreator creator) {
this(player, new ExoPlayable(creator, uri, fileExt));
}
@OptIn(markerClass = UnstableApi.class)
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull ExoPlayable playable) {
super(player);
//noinspection ConstantConditions
if (player.getPlayerView() == null || !(player.getPlayerView() instanceof PlayerView)) {
throw new IllegalArgumentException("Require non-null PlayerView");
// Container is no longer required for constructing new instance.
@SuppressWarnings("unused")
@RemoveIn(version = "3.6.0")
@Deprecated //
public ExoPlayerViewHelper(Container container, @NonNull ToroPlayer player, @NonNull Uri uri) {
this(player, uri);
}
listeners = new MyEventListeners();
this.playable = playable;
this.lazyPrepare = true;
}
@OptIn(markerClass = UnstableApi.class)
@Override protected void initialize(@NonNull PlaybackInfo playbackInfo) {
playable.setPlaybackInfo(playbackInfo);
playable.addEventListener(listeners);
playable.addErrorListener(super.getErrorListeners());
playable.addOnVolumeChangeListener(super.getVolumeChangeListeners());
playable.prepare(!lazyPrepare);
playable.setPlayerView((PlayerView) player.getPlayerView());
}
@OptIn(markerClass = UnstableApi.class)
@Override public void release() {
super.release();
playable.setPlayerView(null);
playable.removeOnVolumeChangeListener(super.getVolumeChangeListeners());
playable.removeErrorListener(super.getErrorListeners());
playable.removeEventListener(listeners);
playable.release();
}
@Override public void play() {
playable.play();
}
@Override public void pause() {
playable.pause();
}
@Override public boolean isPlaying() {
return playable.isPlaying();
}
@Override public void setVolume(float volume) {
playable.setVolume(volume);
}
@Override public float getVolume() {
return playable.getVolume();
}
@Override public void setVolumeInfo(@NonNull VolumeInfo volumeInfo) {
playable.setVolumeInfo(volumeInfo);
}
@Override @NonNull public VolumeInfo getVolumeInfo() {
return playable.getVolumeInfo();
}
@NonNull @Override public PlaybackInfo getLatestPlaybackInfo() {
return playable.getPlaybackInfo();
}
@Override public void setPlaybackInfo(@NonNull PlaybackInfo playbackInfo) {
this.playable.setPlaybackInfo(playbackInfo);
}
@OptIn(markerClass = UnstableApi.class)
public void addEventListener(@NonNull Playable.EventListener listener) {
//noinspection ConstantConditions
if (listener != null) this.listeners.add(listener);
}
@OptIn(markerClass = UnstableApi.class)
public void removeEventListener(Playable.EventListener listener) {
this.listeners.remove(listener);
}
// A proxy, to also hook into ToroPlayerHelper's state change event.
@UnstableApi
private class MyEventListeners extends Playable.EventListeners {
MyEventListeners() {
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri) {
this(player, uri, null);
}
@Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
ExoPlayerViewHelper.super.onPlayerStateUpdated(playWhenReady, playbackState); // important
super.onPlayerStateChanged(playWhenReady, playbackState);
@OptIn(markerClass = UnstableApi.class)
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri,
@Nullable String fileExt) {
this(player, uri, fileExt, with(player.getPlayerView().getContext()).getDefaultCreator());
}
@Override public void onRenderedFirstFrame() {
super.onRenderedFirstFrame();
internalListener.onFirstFrameRendered();
for (ToroPlayer.EventListener listener : ExoPlayerViewHelper.super.getEventListeners()) {
listener.onFirstFrameRendered();
}
/**
* Config instance should be kept as global instance.
*/
@OptIn(markerClass = UnstableApi.class)
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
@NonNull Config config) {
this(player, uri, fileExt,
with(player.getPlayerView().getContext()).getCreator(checkNotNull(config)));
}
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
@NonNull ExoCreator creator) {
this(player, new ExoPlayable(creator, uri, fileExt));
}
@OptIn(markerClass = UnstableApi.class)
public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull ExoPlayable playable) {
super(player);
//noinspection ConstantConditions
if (player.getPlayerView() == null || !(player.getPlayerView() instanceof PlayerView)) {
throw new IllegalArgumentException("Require non-null PlayerView");
}
listeners = new MyEventListeners();
this.playable = playable;
this.lazyPrepare = true;
}
@OptIn(markerClass = UnstableApi.class)
@Override
protected void initialize(@NonNull PlaybackInfo playbackInfo) {
playable.setPlaybackInfo(playbackInfo);
playable.addEventListener(listeners);
playable.addErrorListener(super.getErrorListeners());
playable.addOnVolumeChangeListener(super.getVolumeChangeListeners());
playable.prepare(!lazyPrepare);
playable.setPlayerView((PlayerView) player.getPlayerView());
}
@OptIn(markerClass = UnstableApi.class)
@Override
public void release() {
super.release();
playable.setPlayerView(null);
playable.removeOnVolumeChangeListener(super.getVolumeChangeListeners());
playable.removeErrorListener(super.getErrorListeners());
playable.removeEventListener(listeners);
playable.release();
}
@Override
public void play() {
playable.play();
}
@Override
public void pause() {
playable.pause();
}
@Override
public boolean isPlaying() {
return playable.isPlaying();
}
@Override
public void setVolume(float volume) {
playable.setVolume(volume);
}
@Override
public float getVolume() {
return playable.getVolume();
}
@Override
public void setVolumeInfo(@NonNull VolumeInfo volumeInfo) {
playable.setVolumeInfo(volumeInfo);
}
@Override
@NonNull
public VolumeInfo getVolumeInfo() {
return playable.getVolumeInfo();
}
@NonNull
@Override
public PlaybackInfo getLatestPlaybackInfo() {
return playable.getPlaybackInfo();
}
@Override
public void setPlaybackInfo(@NonNull PlaybackInfo playbackInfo) {
this.playable.setPlaybackInfo(playbackInfo);
}
@OptIn(markerClass = UnstableApi.class)
public void addEventListener(@NonNull Playable.EventListener listener) {
//noinspection ConstantConditions
if (listener != null) this.listeners.add(listener);
}
@OptIn(markerClass = UnstableApi.class)
public void removeEventListener(Playable.EventListener listener) {
this.listeners.remove(listener);
}
public ExoPlayer getPlayer() {
return playable.player.getPlayer();
}
// A proxy, to also hook into ToroPlayerHelper's state change event.
@UnstableApi
private class MyEventListeners extends Playable.EventListeners {
MyEventListeners() {
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
ExoPlayerViewHelper.super.onPlayerStateUpdated(playWhenReady, playbackState); // important
super.onPlayerStateChanged(playWhenReady, playbackState);
}
@Override
public void onRenderedFirstFrame() {
super.onRenderedFirstFrame();
internalListener.onFirstFrameRendered();
for (ToroPlayer.EventListener listener : ExoPlayerViewHelper.super.getEventListeners()) {
listener.onFirstFrameRendered();
}
}
}
}
}

View File

@ -8,6 +8,21 @@
android:layout_gravity="bottom"
android:orientation="vertical">
<ImageView
android:id="@+id/video_quality_exo_playback_control_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_toStartOf="@id/fullscreen_exo_playback_control_view"
android:src="@drawable/ic_video_quality_24dp"
android:background="@drawable/exo_player_control_button_circular_background"
android:clickable="true"
android:focusable="true"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/mute_exo_playback_control_view"
android:layout_width="wrap_content"

View File

@ -56,6 +56,15 @@
android:layout_centerInParent="true"
android:src="@drawable/ic_play_arrow_24dp" />
<ImageView
android:id="@+id/video_quality_exo_playback_control_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:layout_toStartOf="@id/mute_exo_playback_control_view"
android:src="@drawable/ic_video_quality_24dp"
android:visibility="gone" />
<ImageView
android:id="@+id/mute_exo_playback_control_view"
android:layout_width="wrap_content"

View File

@ -96,7 +96,7 @@
</com.google.android.material.button.MaterialButtonGroup>
<com.google.android.material.button.MaterialButton
android:id="@+id/hd_exo_playback_control_view"
android:id="@+id/video_quality_exo_playback_control_view"
style="?attr/materialIconButtonOutlinedStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -124,7 +124,7 @@
<androidx.constraintlayout.widget.Barrier
android:id="@+id/endBarrier"
app:barrierDirection="end"
app:constraint_referenced_ids="hd_exo_playback_control_view"
app:constraint_referenced_ids="video_quality_exo_playback_control_view"
app:barrierAllowsGoneWidgets="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />