Move things back to its original place
This commit is contained in:
parent
fda5405e48
commit
63bcc04eff
35 changed files with 630 additions and 499 deletions
|
|
@ -99,10 +99,22 @@ import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJ
|
|||
@SuppressWarnings({"WeakerAccess"})
|
||||
public abstract class BasePlayer implements
|
||||
Player.EventListener, PlaybackListener, ImageLoadingListener {
|
||||
|
||||
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||
@NonNull
|
||||
public static final String TAG = "BasePlayer";
|
||||
|
||||
public static final int STATE_PREFLIGHT = -1;
|
||||
public static final int STATE_BLOCKED = 123;
|
||||
public static final int STATE_PLAYING = 124;
|
||||
public static final int STATE_BUFFERING = 125;
|
||||
public static final int STATE_PAUSED = 126;
|
||||
public static final int STATE_PAUSED_SEEK = 127;
|
||||
public static final int STATE_COMPLETED = 128;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Intent
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@NonNull
|
||||
public static final String REPEAT_MODE = "repeat_mode";
|
||||
@NonNull
|
||||
|
|
@ -123,26 +135,43 @@ public abstract class BasePlayer implements
|
|||
public static final String START_PAUSED = "start_paused";
|
||||
@NonNull
|
||||
public static final String SELECT_ON_APPEND = "select_on_append";
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Intent
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@NonNull
|
||||
public static final String IS_MUTED = "is_muted";
|
||||
public static final int STATE_PREFLIGHT = -1;
|
||||
public static final int STATE_BLOCKED = 123;
|
||||
public static final int STATE_PLAYING = 124;
|
||||
public static final int STATE_BUFFERING = 125;
|
||||
public static final int STATE_PAUSED = 126;
|
||||
public static final int STATE_PAUSED_SEEK = 127;
|
||||
public static final int STATE_COMPLETED = 128;
|
||||
protected static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f};
|
||||
protected static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds
|
||||
protected static final int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
protected static final int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds
|
||||
|
||||
protected static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1f, 1.25f, 1.5f, 1.75f, 2f};
|
||||
|
||||
protected PlayQueue playQueue;
|
||||
protected PlayQueueAdapter playQueueAdapter;
|
||||
|
||||
@Nullable
|
||||
protected MediaSourceManager playbackManager;
|
||||
|
||||
@Nullable
|
||||
private PlayQueueItem currentItem;
|
||||
@Nullable
|
||||
private MediaSourceTag currentMetadata;
|
||||
@Nullable
|
||||
private Bitmap currentThumbnail;
|
||||
|
||||
@Nullable
|
||||
protected Toast errorToast;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected static final int PLAY_PREV_ACTIVATION_LIMIT_MILLIS = 5000; // 5 seconds
|
||||
protected static final int PROGRESS_LOOP_INTERVAL_MILLIS = 500;
|
||||
|
||||
protected SimpleExoPlayer simpleExoPlayer;
|
||||
protected AudioReactor audioReactor;
|
||||
protected MediaSessionManager mediaSessionManager;
|
||||
|
||||
|
||||
@NonNull
|
||||
protected final Context context;
|
||||
@NonNull
|
||||
|
|
@ -158,39 +187,17 @@ public abstract class BasePlayer implements
|
|||
@NonNull
|
||||
private final LoadControl loadControl;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@NonNull
|
||||
private final RenderersFactory renderFactory;
|
||||
@NonNull
|
||||
private final SerialDisposable progressUpdateReactor;
|
||||
@NonNull
|
||||
private final CompositeDisposable databaseUpdateReactor;
|
||||
protected PlayQueue playQueue;
|
||||
protected PlayQueueAdapter playQueueAdapter;
|
||||
@Nullable
|
||||
protected MediaSourceManager playbackManager;
|
||||
@Nullable
|
||||
protected Toast errorToast;
|
||||
protected SimpleExoPlayer simpleExoPlayer;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
protected AudioReactor audioReactor;
|
||||
protected MediaSessionManager mediaSessionManager;
|
||||
protected int currentState = STATE_PREFLIGHT;
|
||||
@Nullable
|
||||
private PlayQueueItem currentItem;
|
||||
@Nullable
|
||||
private MediaSourceTag currentMetadata;
|
||||
@Nullable
|
||||
private Bitmap currentThumbnail;
|
||||
private boolean isPrepared = false;
|
||||
private Disposable stateLoader;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Thumbnail Loading
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
protected int currentState = STATE_PREFLIGHT;
|
||||
|
||||
public BasePlayer(@NonNull final Context context) {
|
||||
this.context = context;
|
||||
|
|
@ -247,8 +254,7 @@ public abstract class BasePlayer implements
|
|||
registerBroadcastReceiver();
|
||||
}
|
||||
|
||||
public void initListeners() {
|
||||
}
|
||||
public void initListeners() { }
|
||||
|
||||
public void handleIntent(final Intent intent) {
|
||||
if (DEBUG) {
|
||||
|
|
@ -324,10 +330,6 @@ public abstract class BasePlayer implements
|
|||
/*playOnInit=*/!intent.getBooleanExtra(START_PAUSED, false), isMuted);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Broadcast Receiver
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected void initPlayback(@NonNull final PlayQueue queue,
|
||||
@Player.RepeatMode final int repeatMode,
|
||||
final float playbackSpeed,
|
||||
|
|
@ -398,9 +400,12 @@ public abstract class BasePlayer implements
|
|||
|
||||
databaseUpdateReactor.clear();
|
||||
progressUpdateReactor.set(null);
|
||||
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Thumbnail Loading
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void initThumbnail(final String url) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Thumbnail - initThumbnail() called");
|
||||
|
|
@ -413,10 +418,6 @@ public abstract class BasePlayer implements
|
|||
.loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// States Implementation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onLoadingStarted(final String imageUri, final View view) {
|
||||
if (DEBUG) {
|
||||
|
|
@ -453,6 +454,10 @@ public abstract class BasePlayer implements
|
|||
currentThumbnail = null;
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Broadcast Receiver
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Add your action in the intentFilter.
|
||||
*
|
||||
|
|
@ -488,6 +493,10 @@ public abstract class BasePlayer implements
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// States Implementation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void changeState(final int state) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "changeState() called with: state = [" + state + "]");
|
||||
|
|
@ -1328,6 +1337,7 @@ public abstract class BasePlayer implements
|
|||
playQueue.append(autoQueue.getStreams());
|
||||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Getters and Setters
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
|
|
|||
|
|
@ -1181,11 +1181,14 @@ public final class MainVideoPlayer extends AppCompatActivity
|
|||
private class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener
|
||||
implements View.OnTouchListener {
|
||||
private static final int MOVEMENT_THRESHOLD = 40;
|
||||
|
||||
private final boolean isVolumeGestureEnabled = PlayerHelper
|
||||
.isVolumeGestureEnabled(getApplicationContext());
|
||||
private final boolean isBrightnessGestureEnabled = PlayerHelper
|
||||
.isBrightnessGestureEnabled(getApplicationContext());
|
||||
|
||||
private final int maxVolume = playerImpl.getAudioReactor().getMaxVolume();
|
||||
|
||||
private boolean isMoving;
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -54,14 +54,19 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
|||
View.OnClickListener, PlaybackParameterDialog.Callback {
|
||||
private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47;
|
||||
private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80;
|
||||
|
||||
protected BasePlayer player;
|
||||
|
||||
private boolean serviceBound;
|
||||
private ServiceConnection serviceConnection;
|
||||
|
||||
private boolean seeking;
|
||||
private boolean redraw;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
private boolean seeking;
|
||||
private boolean redraw;
|
||||
|
||||
private View rootView;
|
||||
|
||||
private RecyclerView itemsList;
|
||||
|
|
|
|||
|
|
@ -90,51 +90,69 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
Player.EventListener,
|
||||
PopupMenu.OnMenuItemClickListener,
|
||||
PopupMenu.OnDismissListener {
|
||||
public final String TAG;
|
||||
public static final boolean DEBUG = BasePlayer.DEBUG;
|
||||
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Player
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static final int DEFAULT_CONTROLS_DURATION = 300; // 300 millis
|
||||
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
|
||||
protected static final int RENDERER_UNAVAILABLE = -1;
|
||||
public final String TAG;
|
||||
|
||||
@NonNull
|
||||
private final VideoPlaybackResolver resolver;
|
||||
private final Handler controlsVisibilityHandler = new Handler();
|
||||
private final int qualityPopupMenuGroupId = 69;
|
||||
private final int playbackSpeedPopupMenuGroupId = 79;
|
||||
|
||||
private List<VideoStream> availableStreams;
|
||||
private int selectedStreamIndex;
|
||||
|
||||
protected boolean wasPlaying = false;
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
private final int captionPopupMenuGroupId = 89;
|
||||
protected boolean wasPlaying = false;
|
||||
boolean isSomePopupMenuVisible = false;
|
||||
private List<VideoStream> availableStreams;
|
||||
private int selectedStreamIndex;
|
||||
|
||||
private View rootView;
|
||||
|
||||
private AspectRatioFrameLayout aspectRatioFrameLayout;
|
||||
private SurfaceView surfaceView;
|
||||
private View surfaceForeground;
|
||||
|
||||
private View loadingPanel;
|
||||
private ImageView endScreen;
|
||||
private ImageView controlAnimationView;
|
||||
|
||||
private View controlsRoot;
|
||||
private TextView currentDisplaySeek;
|
||||
|
||||
private View bottomControlsRoot;
|
||||
private SeekBar playbackSeekBar;
|
||||
private TextView playbackCurrentTime;
|
||||
private TextView playbackEndTime;
|
||||
private TextView playbackLiveSync;
|
||||
private TextView playbackSpeedTextView;
|
||||
|
||||
private View topControlsRoot;
|
||||
private TextView qualityTextView;
|
||||
|
||||
private SubtitleView subtitleView;
|
||||
|
||||
private TextView resizeView;
|
||||
private TextView captionTextView;
|
||||
|
||||
private ValueAnimator controlViewAnimator;
|
||||
private final Handler controlsVisibilityHandler = new Handler();
|
||||
|
||||
boolean isSomePopupMenuVisible = false;
|
||||
|
||||
private final int qualityPopupMenuGroupId = 69;
|
||||
private PopupMenu qualityPopupMenu;
|
||||
|
||||
private final int playbackSpeedPopupMenuGroupId = 79;
|
||||
private PopupMenu playbackSpeedPopupMenu;
|
||||
|
||||
private final int captionPopupMenuGroupId = 89;
|
||||
private PopupMenu captionPopupMenu;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -238,10 +256,6 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// UI Builders
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void handleIntent(final Intent intent) {
|
||||
if (intent == null) {
|
||||
|
|
@ -255,6 +269,10 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
super.handleIntent(intent);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// UI Builders
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void buildQualityMenu() {
|
||||
if (qualityPopupMenu == null) {
|
||||
return;
|
||||
|
|
@ -354,9 +372,6 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
}
|
||||
captionPopupMenu.setOnDismissListener(this);
|
||||
}
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void updateStreamRelatedViews() {
|
||||
if (getCurrentMetadata() == null) {
|
||||
|
|
@ -413,6 +428,10 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
playbackSpeedTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
protected abstract VideoPlaybackResolver.QualityResolver getQualityResolver();
|
||||
|
||||
protected void onMetadataChanged(@NonNull final MediaSourceTag tag) {
|
||||
|
|
@ -420,16 +439,16 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
updateStreamRelatedViews();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// States Implementation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
|
||||
return resolver.resolve(info);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// States Implementation
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onBlocked() {
|
||||
super.onBlocked();
|
||||
|
|
@ -494,10 +513,6 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
showAndAnimateControl(-1, true);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// ExoPlayer Video Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCompleted() {
|
||||
super.onCompleted();
|
||||
|
|
@ -510,6 +525,10 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
animateView(surfaceForeground, true, 100);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// ExoPlayer Video Listener
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onTracksChanged(final TrackGroupArray trackGroups,
|
||||
final TrackSelectionArray trackSelections) {
|
||||
|
|
@ -537,15 +556,15 @@ public abstract class VideoPlayer extends BasePlayer
|
|||
aspectRatioFrameLayout.setAspectRatio(((float) width) / height);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// ExoPlayer Track Updates
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onRenderedFirstFrame() {
|
||||
animateView(surfaceForeground, false, 100);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// ExoPlayer Track Updates
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void onTextTrackUpdate() {
|
||||
final int textRenderer = getRendererIndex(C.TRACK_TYPE_TEXT);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,17 +20,20 @@ import java.io.File;
|
|||
|
||||
/* package-private */ class CacheFactory implements DataSource.Factory {
|
||||
private static final String TAG = "CacheFactory";
|
||||
|
||||
private static final String CACHE_FOLDER_NAME = "exoplayer";
|
||||
private static final int CACHE_FLAGS = CacheDataSource.FLAG_BLOCK_ON_CACHE
|
||||
| CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR;
|
||||
|
||||
private final DefaultDataSourceFactory dataSourceFactory;
|
||||
private final File cacheDir;
|
||||
private final long maxFileSize;
|
||||
|
||||
// Creating cache on every instance may cause problems with multiple players when
|
||||
// sources are not ExtractorMediaSource
|
||||
// see: https://stackoverflow.com/questions/28700391/using-cache-in-exoplayer
|
||||
// todo: make this a singleton?
|
||||
private static SimpleCache cache;
|
||||
private final DefaultDataSourceFactory dataSourceFactory;
|
||||
private final File cacheDir;
|
||||
private final long maxFileSize;
|
||||
|
||||
CacheFactory(@NonNull final Context context,
|
||||
@NonNull final String userAgent,
|
||||
|
|
|
|||
|
|
@ -23,19 +23,23 @@ import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
|||
|
||||
public class PlaybackParameterDialog extends DialogFragment {
|
||||
// Minimum allowable range in ExoPlayer
|
||||
public static final double MINIMUM_PLAYBACK_VALUE = 0.10f;
|
||||
public static final double MAXIMUM_PLAYBACK_VALUE = 3.00f;
|
||||
public static final char STEP_UP_SIGN = '+';
|
||||
public static final char STEP_DOWN_SIGN = '-';
|
||||
public static final double STEP_ONE_PERCENT_VALUE = 0.01f;
|
||||
public static final double STEP_FIVE_PERCENT_VALUE = 0.05f;
|
||||
public static final double STEP_TEN_PERCENT_VALUE = 0.10f;
|
||||
public static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f;
|
||||
public static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f;
|
||||
public static final double DEFAULT_TEMPO = 1.00f;
|
||||
public static final double DEFAULT_PITCH = 1.00f;
|
||||
public static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE;
|
||||
public static final boolean DEFAULT_SKIP_SILENCE = false;
|
||||
private static final double MINIMUM_PLAYBACK_VALUE = 0.10f;
|
||||
private static final double MAXIMUM_PLAYBACK_VALUE = 3.00f;
|
||||
|
||||
private static final char STEP_UP_SIGN = '+';
|
||||
private static final char STEP_DOWN_SIGN = '-';
|
||||
|
||||
private static final double STEP_ONE_PERCENT_VALUE = 0.01f;
|
||||
private static final double STEP_FIVE_PERCENT_VALUE = 0.05f;
|
||||
private static final double STEP_TEN_PERCENT_VALUE = 0.10f;
|
||||
private static final double STEP_TWENTY_FIVE_PERCENT_VALUE = 0.25f;
|
||||
private static final double STEP_ONE_HUNDRED_PERCENT_VALUE = 1.00f;
|
||||
|
||||
private static final double DEFAULT_TEMPO = 1.00f;
|
||||
private static final double DEFAULT_PITCH = 1.00f;
|
||||
private static final double DEFAULT_STEP = STEP_TWENTY_FIVE_PERCENT_VALUE;
|
||||
private static final boolean DEFAULT_SKIP_SILENCE = false;
|
||||
|
||||
@NonNull
|
||||
private static final String TAG = "PlaybackParameterDialog";
|
||||
@NonNull
|
||||
|
|
@ -49,18 +53,22 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
private static final String PITCH_KEY = "pitch_key";
|
||||
@NonNull
|
||||
private static final String STEP_SIZE_KEY = "step_size_key";
|
||||
|
||||
@NonNull
|
||||
private final SliderStrategy strategy = new SliderStrategy.Quadratic(
|
||||
MINIMUM_PLAYBACK_VALUE, MAXIMUM_PLAYBACK_VALUE,
|
||||
/*centerAt=*/1.00f, /*sliderGranularity=*/10000);
|
||||
|
||||
@Nullable
|
||||
private Callback callback;
|
||||
|
||||
private double initialTempo = DEFAULT_TEMPO;
|
||||
private double initialPitch = DEFAULT_PITCH;
|
||||
private boolean initialSkipSilence = DEFAULT_SKIP_SILENCE;
|
||||
private double tempo = DEFAULT_TEMPO;
|
||||
private double pitch = DEFAULT_PITCH;
|
||||
private double stepSize = DEFAULT_STEP;
|
||||
|
||||
@Nullable
|
||||
private SeekBar tempoSlider;
|
||||
@Nullable
|
||||
|
|
@ -96,25 +104,10 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
return dialog;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getStepUpPercentString(final double percent) {
|
||||
return STEP_UP_SIGN + getPercentString(percent);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Lifecycle
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@NonNull
|
||||
private static String getStepDownPercentString(final double percent) {
|
||||
return STEP_DOWN_SIGN + getPercentString(percent);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getPercentString(final double percent) {
|
||||
return PlayerHelper.formatPitch(percent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(final Context context) {
|
||||
super.onAttach(context);
|
||||
|
|
@ -125,10 +118,6 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Dialog
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
assureCorrectAppLanguage(getContext());
|
||||
|
|
@ -143,10 +132,6 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Control Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(final Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
|
@ -158,6 +143,10 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
outState.putDouble(STEP_SIZE_KEY, getCurrentStepSize());
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Dialog
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
|
||||
|
|
@ -179,6 +168,10 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
return dialogBuilder.create();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Control Views
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setupControlViews(@NonNull final View rootView) {
|
||||
setupHookingControl(rootView);
|
||||
setupSkipSilenceControl(rootView);
|
||||
|
|
@ -273,10 +266,6 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Sliders
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setupStepSizeSelector(@NonNull final View rootView) {
|
||||
TextView stepSizeOnePercentText = rootView.findViewById(R.id.stepSizeOnePercent);
|
||||
TextView stepSizeFivePercentText = rootView.findViewById(R.id.stepSizeFivePercent);
|
||||
|
|
@ -355,6 +344,10 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Sliders
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private SeekBar.OnSeekBarChangeListener getOnTempoChangedListener() {
|
||||
return new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
|
|
@ -430,10 +423,6 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
setPitchSlider(newValue);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Helper
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setTempoSlider(final double newTempo) {
|
||||
if (tempoSlider == null) {
|
||||
return;
|
||||
|
|
@ -448,6 +437,10 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
pitchSlider.setProgress(strategy.progressOf(newPitch));
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Helper
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void setCurrentPlaybackParameters() {
|
||||
setPlaybackParameters(getCurrentTempo(), getCurrentPitch(), getCurrentSkipSilence());
|
||||
}
|
||||
|
|
@ -483,6 +476,21 @@ public class PlaybackParameterDialog extends DialogFragment {
|
|||
return skipSilenceCheckbox != null && skipSilenceCheckbox.isChecked();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getStepUpPercentString(final double percent) {
|
||||
return STEP_UP_SIGN + getPercentString(percent);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getStepDownPercentString(final double percent) {
|
||||
return STEP_DOWN_SIGN + getPercentString(percent);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String getPercentString(final double percent) {
|
||||
return PlayerHelper.formatPitch(percent);
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onPlaybackParameterChanged(float playbackTempo, float playbackPitch,
|
||||
boolean playbackSkipSilence);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ public final class PlayerHelper {
|
|||
|
||||
private PlayerHelper() { }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Exposed helpers
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static String getTimeString(final int milliSeconds) {
|
||||
int seconds = (milliSeconds % 60000) / 1000;
|
||||
int minutes = (milliSeconds % 3600000) / 60000;
|
||||
|
|
@ -72,9 +76,6 @@ public final class PlayerHelper {
|
|||
? STRING_FORMATTER.format("%d:%02d:%02d", hours, minutes, seconds).toString()
|
||||
: STRING_FORMATTER.format("%02d:%02d", minutes, seconds).toString();
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Exposed helpers
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static String formatSpeed(final double speed) {
|
||||
return SPEED_FORMATTER.format(speed);
|
||||
|
|
@ -177,14 +178,14 @@ public final class PlayerHelper {
|
|||
? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0));
|
||||
}
|
||||
|
||||
public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) {
|
||||
return isResumeAfterAudioFocusGain(context, false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Settings Resolution
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) {
|
||||
return isResumeAfterAudioFocusGain(context, false);
|
||||
}
|
||||
|
||||
public static boolean isVolumeGestureEnabled(@NonNull final Context context) {
|
||||
return isVolumeGestureEnabled(context, true);
|
||||
}
|
||||
|
|
@ -322,15 +323,15 @@ public final class PlayerHelper {
|
|||
setScreenBrightness(context, setScreenBrightness, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private helpers
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@NonNull
|
||||
private static SharedPreferences getPreferences(@NonNull final Context context) {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Private helpers
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static boolean isResumeAfterAudioFocusGain(@NonNull final Context context,
|
||||
final boolean b) {
|
||||
return getPreferences(context)
|
||||
|
|
|
|||
|
|
@ -44,16 +44,21 @@ import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfo
|
|||
import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG;
|
||||
|
||||
public class MediaSourceManager {
|
||||
@NonNull
|
||||
private final String TAG = "MediaSourceManager@" + hashCode();
|
||||
|
||||
/**
|
||||
* Determines how many streams before and after the current stream should be loaded.
|
||||
* The default value (1) ensures seamless playback under typical network settings.
|
||||
* <br><br>
|
||||
* <p>
|
||||
* The streams after the current will be loaded into the playlist timeline while the
|
||||
* streams before will only be cached for future usage.
|
||||
* </p>
|
||||
*
|
||||
* @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource)
|
||||
*/
|
||||
private static final int WINDOW_SIZE = 1;
|
||||
|
||||
/**
|
||||
* Determines the maximum number of disposables allowed in the {@link #loaderReactor}.
|
||||
* Once exceeded, new calls to {@link #loadImmediate()} will evict all disposables in the
|
||||
|
|
@ -63,12 +68,12 @@ public class MediaSourceManager {
|
|||
* @see #maybeLoadItem(PlayQueueItem)
|
||||
*/
|
||||
private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1;
|
||||
@NonNull
|
||||
private final String TAG = "MediaSourceManager@" + hashCode();
|
||||
|
||||
@NonNull
|
||||
private final PlaybackListener playbackListener;
|
||||
@NonNull
|
||||
private final PlayQueue playQueue;
|
||||
|
||||
/**
|
||||
* Determines the gap time between the playback position and the playback duration which
|
||||
* the {@link #getEdgeIntervalSignal()} begins to request loading.
|
||||
|
|
@ -76,35 +81,45 @@ public class MediaSourceManager {
|
|||
* @see #progressUpdateIntervalMillis
|
||||
*/
|
||||
private final long playbackNearEndGapMillis;
|
||||
|
||||
/**
|
||||
* Determines the interval which the {@link #getEdgeIntervalSignal()} waits for between
|
||||
* each request for loading, once {@link #playbackNearEndGapMillis} has reached.
|
||||
*/
|
||||
private final long progressUpdateIntervalMillis;
|
||||
|
||||
@NonNull
|
||||
private final Observable<Long> nearEndIntervalSignal;
|
||||
|
||||
/**
|
||||
* Process only the last load order when receiving a stream of load orders (lessens I/O).
|
||||
* <br><br>
|
||||
* <p>
|
||||
* The higher it is, the less loading occurs during rapid noncritical timeline changes.
|
||||
* <br><br>
|
||||
* </p>
|
||||
* <p>
|
||||
* Not recommended to go below 100ms.
|
||||
* </p>
|
||||
*
|
||||
* @see #loadDebounced()
|
||||
*/
|
||||
private final long loadDebounceMillis;
|
||||
|
||||
@NonNull
|
||||
private final Disposable debouncedLoader;
|
||||
@NonNull
|
||||
private final PublishSubject<Long> debouncedSignal;
|
||||
|
||||
@NonNull
|
||||
private Subscription playQueueReactor;
|
||||
|
||||
@NonNull
|
||||
private final CompositeDisposable loaderReactor;
|
||||
@NonNull
|
||||
private final Set<PlayQueueItem> loadingItems;
|
||||
|
||||
@NonNull
|
||||
private final AtomicBoolean isBlocked;
|
||||
@NonNull
|
||||
private Subscription playQueueReactor;
|
||||
|
||||
@NonNull
|
||||
private ManagedMediaSourcePlaylist playlist;
|
||||
|
||||
|
|
@ -160,42 +175,6 @@ public class MediaSourceManager {
|
|||
// Exposed Methods
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Manager Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
@Nullable
|
||||
private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) {
|
||||
// The current item has higher priority
|
||||
final int currentIndex = playQueue.getIndex();
|
||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||
if (currentItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The rest are just for seamless playback
|
||||
// Although timeline is not updated prior to the current index, these sources are still
|
||||
// loaded into the cache for faster retrieval at a potentially later time.
|
||||
final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE);
|
||||
final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1;
|
||||
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
||||
final Set<PlayQueueItem> neighbors = new ArraySet<>(
|
||||
playQueue.getStreams().subList(leftBound, rightBound));
|
||||
|
||||
// Do a round robin
|
||||
final int excess = rightLimit - playQueue.size();
|
||||
if (excess >= 0) {
|
||||
neighbors.addAll(playQueue.getStreams()
|
||||
.subList(0, Math.min(playQueue.size(), excess)));
|
||||
}
|
||||
neighbors.remove(currentItem);
|
||||
|
||||
return new ItemsToLoad(currentItem, neighbors);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Event Reactor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
/**
|
||||
* Dispose the manager and releases all message buses and loaders.
|
||||
*/
|
||||
|
|
@ -211,6 +190,10 @@ public class MediaSourceManager {
|
|||
loaderReactor.dispose();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Event Reactor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private Subscriber<PlayQueueEvent> getReactor() {
|
||||
return new Subscriber<PlayQueueEvent>() {
|
||||
@Override
|
||||
|
|
@ -233,10 +216,6 @@ public class MediaSourceManager {
|
|||
};
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Locking
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void onPlayQueueChanged(final PlayQueueEvent event) {
|
||||
if (playQueue.isEmpty() && playQueue.isComplete()) {
|
||||
playbackListener.onPlaybackShutdown();
|
||||
|
|
@ -298,6 +277,10 @@ public class MediaSourceManager {
|
|||
playQueueReactor.request(1);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Playback Locking
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private boolean isPlayQueueReady() {
|
||||
final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > WINDOW_SIZE;
|
||||
return playQueue.isComplete() || isWindowLoaded;
|
||||
|
|
@ -332,10 +315,6 @@ public class MediaSourceManager {
|
|||
isBlocked.set(true);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Metadata Synchronization
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void maybeUnblock() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "maybeUnblock() called.");
|
||||
|
|
@ -347,6 +326,10 @@ public class MediaSourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Metadata Synchronization
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void maybeSync() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "maybeSync() called.");
|
||||
|
|
@ -360,10 +343,6 @@ public class MediaSourceManager {
|
|||
playbackListener.onPlaybackSynchronize(currentItem);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Loading
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private synchronized void maybeSynchronizePlayer() {
|
||||
if (isPlayQueueReady() && isPlaybackReady()) {
|
||||
maybeUnblock();
|
||||
|
|
@ -371,6 +350,10 @@ public class MediaSourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Loading
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private Observable<Long> getEdgeIntervalSignal() {
|
||||
return Observable.interval(progressUpdateIntervalMillis, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
@ -523,9 +506,6 @@ public class MediaSourceManager {
|
|||
}
|
||||
playlist.invalidate(currentIndex, removeMediaSourceHandler, this::loadImmediate);
|
||||
}
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Playlist Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void maybeClearLoaders() {
|
||||
if (DEBUG) {
|
||||
|
|
@ -538,6 +518,10 @@ public class MediaSourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// MediaSource Playlist Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
private void resetSources() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "resetSources() called.");
|
||||
|
|
@ -554,6 +538,39 @@ public class MediaSourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Manager Helpers
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Nullable
|
||||
private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) {
|
||||
// The current item has higher priority
|
||||
final int currentIndex = playQueue.getIndex();
|
||||
final PlayQueueItem currentItem = playQueue.getItem(currentIndex);
|
||||
if (currentItem == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The rest are just for seamless playback
|
||||
// Although timeline is not updated prior to the current index, these sources are still
|
||||
// loaded into the cache for faster retrieval at a potentially later time.
|
||||
final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE);
|
||||
final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1;
|
||||
final int rightBound = Math.min(playQueue.size(), rightLimit);
|
||||
final Set<PlayQueueItem> neighbors = new ArraySet<>(
|
||||
playQueue.getStreams().subList(leftBound, rightBound));
|
||||
|
||||
// Do a round robin
|
||||
final int excess = rightLimit - playQueue.size();
|
||||
if (excess >= 0) {
|
||||
neighbors.addAll(playQueue.getStreams()
|
||||
.subList(0, Math.min(playQueue.size(), excess)));
|
||||
}
|
||||
neighbors.remove(currentItem);
|
||||
|
||||
return new ItemsToLoad(currentItem, neighbors);
|
||||
}
|
||||
|
||||
private static class ItemsToLoad {
|
||||
@NonNull
|
||||
private final PlayQueueItem center;
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@ import io.reactivex.annotations.NonNull;
|
|||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> extends PlayQueue {
|
||||
final int serviceId;
|
||||
final String baseUrl;
|
||||
boolean isInitial;
|
||||
private boolean isComplete;
|
||||
|
||||
final int serviceId;
|
||||
final String baseUrl;
|
||||
String nextUrl;
|
||||
|
||||
private transient Disposable fetchReactor;
|
||||
|
|
@ -40,16 +41,6 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
|||
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
|
||||
}
|
||||
|
||||
private static List<PlayQueueItem> extractListItems(final List<StreamInfoItem> infos) {
|
||||
List<PlayQueueItem> result = new ArrayList<>();
|
||||
for (final InfoItem stream : infos) {
|
||||
if (stream instanceof StreamInfoItem) {
|
||||
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract String getTag();
|
||||
|
||||
@Override
|
||||
|
|
@ -134,4 +125,14 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
|
|||
}
|
||||
fetchReactor = null;
|
||||
}
|
||||
|
||||
private static List<PlayQueueItem> extractListItems(final List<StreamInfoItem> infos) {
|
||||
List<PlayQueueItem> result = new ArrayList<>();
|
||||
for (final InfoItem stream : infos) {
|
||||
if (stream instanceof StreamInfoItem) {
|
||||
result.add(new PlayQueueItem((StreamInfoItem) stream));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,17 +36,22 @@ import io.reactivex.subjects.BehaviorSubject;
|
|||
* <p>
|
||||
* This class contains basic manipulation of a playlist while also functions as a
|
||||
* message bus, providing all listeners with new updates to the play queue.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class can be serialized for passing intents, but in order to start the
|
||||
* message bus, it must be initialized.
|
||||
* </p>
|
||||
*/
|
||||
public abstract class PlayQueue implements Serializable {
|
||||
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||
private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode());
|
||||
@NonNull
|
||||
private final AtomicInteger queueIndex;
|
||||
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
|
||||
|
||||
private ArrayList<PlayQueueItem> backup;
|
||||
private ArrayList<PlayQueueItem> streams;
|
||||
|
||||
@NonNull
|
||||
private final AtomicInteger queueIndex;
|
||||
|
||||
private transient BehaviorSubject<PlayQueueEvent> eventBroadcast;
|
||||
private transient Flowable<PlayQueueEvent> broadcastReceiver;
|
||||
private transient Subscription reportingReactor;
|
||||
|
|
|
|||
|
|
@ -28,19 +28,23 @@ import io.reactivex.disposables.Disposable;
|
|||
* <p>
|
||||
* Copyright (C) Christian Schabesberger 2016 <chris.schabesberger@mailbox.org>
|
||||
* InfoListAdapter.java is part of NewPipe.
|
||||
* </p>
|
||||
* <p>
|
||||
* NewPipe is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* </p>
|
||||
* <p>
|
||||
* NewPipe is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* </p>
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
|
||||
* </p>
|
||||
*/
|
||||
|
||||
public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public class VideoPlaybackResolver implements PlaybackResolver {
|
|||
private final PlayerDataSource dataSource;
|
||||
@NonNull
|
||||
private final QualityResolver qualityResolver;
|
||||
|
||||
@Nullable
|
||||
private String playbackQuality;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue