1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-10-19 01:17:39 +00:00

-Reverted manual track selection from exoplayer track selector.

-Added quality record to play queue items.
-Added quality and recovery record play queue events.
-Added landscape view for ServicePlayerActivity.
-Moved repeat and shuffle button to play queue panel in main video player.
-Fixed potential NPE in MediaSourceManager by no longer nulling play queue on dispose.
-Renamed PlayQueueEvent to PlayQueueEventType.
-Renamed PlayQueueMessage to PlayQueueEvent.
This commit is contained in:
John Zhen Mo
2017-10-22 18:58:01 -07:00
parent 4553850412
commit 9068247856
32 changed files with 696 additions and 358 deletions

View File

@@ -788,16 +788,14 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
((HistoryListener) activity).onVideoPlayed(currentInfo, getSelectedVideoStream());
}
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
final VideoStream candidate = sortedStreamVideosList.get(actionBarHandler.getSelectedVideoStream());
final PlayQueue playQueue = new SinglePlayQueue(currentInfo, actionBarHandler.getSelectedVideoStream());
final Intent intent;
if (append) {
Toast.makeText(activity, R.string.popup_playing_append, Toast.LENGTH_SHORT).show();
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, true);
intent = NavigationHelper.getPlayerEnqueueIntent(activity, PopupVideoPlayer.class, playQueue);
} else {
Toast.makeText(activity, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show();
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue, Localization.resolutionOf(candidate.resolution));
intent = NavigationHelper.getPlayerIntent(activity, PopupVideoPlayer.class, playQueue);
}
activity.startService(intent);
}
@@ -819,10 +817,11 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
private void openNormalBackgroundPlayer(final boolean append) {
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
activity.startService(NavigationHelper.getPlayerIntent(activity, BackgroundPlayer.class, playQueue, append));
if (append) {
activity.startService(NavigationHelper.getPlayerEnqueueIntent(activity, BackgroundPlayer.class, playQueue));
Toast.makeText(activity, R.string.background_player_append, Toast.LENGTH_SHORT).show();
} else {
activity.startService(NavigationHelper.getPlayerIntent(activity, BackgroundPlayer.class, playQueue));
Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
}
}
@@ -867,9 +866,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
|| (Build.VERSION.SDK_INT < 16);
if (!useOldPlayer) {
// ExoPlayer
final PlayQueue playQueue = new SinglePlayQueue(currentInfo);
final VideoStream candidate = sortedStreamVideosList.get(actionBarHandler.getSelectedVideoStream());
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue, Localization.resolutionOf(candidate.resolution));
final PlayQueue playQueue = new SinglePlayQueue(currentInfo, actionBarHandler.getSelectedVideoStream());
mIntent = NavigationHelper.getPlayerIntent(activity, MainVideoPlayer.class, playQueue);
} else {
// Internal Player
mIntent = new Intent(activity, PlayVideoActivity.class)

View File

@@ -1,11 +1,13 @@
package org.schabi.newpipe.fragments.list.playlist;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -14,6 +16,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.ListExtractor;
@@ -29,6 +32,7 @@ import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import io.reactivex.Single;
@@ -162,6 +166,13 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
headerPopupButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !PermissionHelper.checkSystemAlertWindowPermission(activity)) {
Toast toast = Toast.makeText(activity, R.string.msg_popup_permission, Toast.LENGTH_LONG);
TextView messageView = toast.getView().findViewById(android.R.id.message);
if (messageView != null) messageView.setGravity(Gravity.CENTER);
toast.show();
return;
}
activity.startService(buildPlaylistIntent(PopupVideoPlayer.class));
}
});

View File

@@ -290,7 +290,9 @@ public final class BackgroundPlayer extends Service {
}
@Override
protected void postProcess(@NonNull final Intent intent) {
public void handleIntent(final Intent intent) {
super.handleIntent(intent);
resetNotification();
startForeground(NOTIFICATION_ID, notBuilder.build());
@@ -437,7 +439,7 @@ public final class BackgroundPlayer extends Service {
}
@Override
public MediaSource sourceOf(final StreamInfo info) {
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final int index = ListHelper.getDefaultAudioFormat(context, info.audio_streams);
if (index < 0) return null;

View File

@@ -122,6 +122,8 @@ public abstract class BasePlayer implements Player.EventListener,
// Intent
//////////////////////////////////////////////////////////////////////////*/
public static final String REPEAT_MODE = "repeat_mode";
public static final String PLAYBACK_PITCH = "playback_pitch";
public static final String PLAYBACK_SPEED = "playback_speed";
public static final String PLAY_QUEUE = "play_queue";
public static final String APPEND_ONLY = "append_only";
@@ -234,8 +236,6 @@ public abstract class BasePlayer implements Player.EventListener,
});
}
protected abstract void postProcess(@NonNull final Intent intent);
public void handleIntent(Intent intent) {
if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
if (intent == null) return;
@@ -253,6 +253,7 @@ public abstract class BasePlayer implements Player.EventListener,
}
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
setPlaybackPitch(intent.getFloatExtra(PLAYBACK_PITCH, getPlaybackPitch()));
// Re-initialization
destroyPlayer();
@@ -262,7 +263,6 @@ public abstract class BasePlayer implements Player.EventListener,
// Good to go...
initPlayback(this, queue);
postProcess(intent);
}
protected void initPlayback(@NonNull final PlaybackListener listener, @NonNull final PlayQueue queue) {
@@ -288,7 +288,6 @@ public abstract class BasePlayer implements Player.EventListener,
});
}
public void onThumbnailReceived(Bitmap thumbnail) {
if (DEBUG) Log.d(TAG, "onThumbnailReceived() called with: thumbnail = [" + thumbnail + "]");
}
@@ -470,7 +469,6 @@ public abstract class BasePlayer implements Player.EventListener,
public static final int STATE_PAUSED_SEEK = 127;
public static final int STATE_COMPLETED = 128;
protected int currentState = -1;
public void changeState(int state) {
@@ -577,15 +575,13 @@ public abstract class BasePlayer implements Player.EventListener,
// Check if recovering
if (isCurrentWindowCorrect && currentSourceItem != null &&
currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
/* Recovering with sub-second position may cause a long buffer delay in ExoPlayer,
* rounding this position to the nearest second will help alleviate this.*/
final long position = currentSourceItem.getRecoveryPosition();
// todo: figure out exactly why this is the case
/* Rounding time to nearest second as certain media cannot guarantee a sub-second seek
will complete and the player might get stuck in buffering state forever */
final long roundedPos = (currentSourceItem.getRecoveryPosition() / 1000) * 1000;
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)roundedPos));
if (DEBUG) Log.d(TAG, "Rewinding to recovery window: " + currentSourceIndex + " at: " + getTimeString((int)position));
simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition());
currentSourceItem.resetRecoveryPosition();
playQueue.unsetRecovery(currentSourceIndex);
}
}
@@ -995,10 +991,6 @@ public abstract class BasePlayer implements Player.EventListener,
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch));
}
public int getCurrentResolutionTarget() {
return trackSelector != null ? trackSelector.getParameters().maxVideoHeight : Integer.MAX_VALUE;
}
public PlayQueue getPlayQueue() {
return playQueue;
}
@@ -1024,6 +1016,6 @@ public abstract class BasePlayer implements Player.EventListener,
if (playQueue.size() <= queuePos) return;
if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos);
playQueue.getItem(queuePos).setRecoveryPosition(windowPos);
playQueue.setRecovery(queuePos, windowPos);
}
}

View File

@@ -28,7 +28,6 @@ import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
@@ -52,7 +51,6 @@ import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
import org.schabi.newpipe.playlist.PlayQueueItemHolder;
import org.schabi.newpipe.util.AnimationUtils;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
@@ -208,6 +206,15 @@ public final class MainVideoPlayer extends Activity {
}
}
protected void setShuffleButton(final ImageButton shuffleButton, final boolean shuffled) {
final int shuffleAlpha = shuffled ? 255 : 77;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
shuffleButton.setImageAlpha(shuffleAlpha);
} else {
shuffleButton.setAlpha(shuffleAlpha);
}
}
///////////////////////////////////////////////////////////////////////////
@SuppressWarnings({"unused", "WeakerAccess"})
@@ -216,8 +223,9 @@ public final class MainVideoPlayer extends Activity {
private TextView channelTextView;
private TextView volumeTextView;
private TextView brightnessTextView;
private ImageButton repeatButton;
private ImageButton queueButton;
private ImageButton repeatButton;
private ImageButton shuffleButton;
private ImageButton screenRotationButton;
private ImageButton playPauseButton;
@@ -242,8 +250,9 @@ public final class MainVideoPlayer extends Activity {
this.channelTextView = rootView.findViewById(R.id.channelTextView);
this.volumeTextView = rootView.findViewById(R.id.volumeTextView);
this.brightnessTextView = rootView.findViewById(R.id.brightnessTextView);
this.repeatButton = rootView.findViewById(R.id.repeatButton);
this.queueButton = rootView.findViewById(R.id.queueButton);
this.repeatButton = rootView.findViewById(R.id.repeatButton);
this.shuffleButton = rootView.findViewById(R.id.shuffleButton);
this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
@@ -264,18 +273,14 @@ public final class MainVideoPlayer extends Activity {
queueButton.setOnClickListener(this);
repeatButton.setOnClickListener(this);
shuffleButton.setOnClickListener(this);
playPauseButton.setOnClickListener(this);
playPreviousButton.setOnClickListener(this);
playNextButton.setOnClickListener(this);
screenRotationButton.setOnClickListener(this);
}
@Override
public int getPreferredResolution() {
if (sharedPreferences == null || context == null) return Integer.MAX_VALUE;
return Localization.resolutionOf(sharedPreferences.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value)));
}
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@@ -283,7 +288,7 @@ public final class MainVideoPlayer extends Activity {
@Override
public void onRepeatModeChanged(int i) {
super.onRepeatModeChanged(i);
setRepeatModeButton(repeatButton, simpleExoPlayer.getRepeatMode());
updatePlaybackButtons();
}
/*//////////////////////////////////////////////////////////////////////////
@@ -305,6 +310,12 @@ public final class MainVideoPlayer extends Activity {
playPauseButton.setImageResource(R.drawable.ic_pause_white);
}
@Override
public void onShuffleClicked() {
super.onShuffleClicked();
updatePlaybackButtons();
}
@Override
public void onFullScreenButtonClicked() {
super.onFullScreenButtonClicked();
@@ -323,8 +334,9 @@ public final class MainVideoPlayer extends Activity {
context,
PopupVideoPlayer.class,
this.getPlayQueue(),
this.getCurrentResolutionTarget(),
this.getPlaybackSpeed()
this.simpleExoPlayer.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch()
);
context.startService(intent);
destroyPlayer();
@@ -336,10 +348,7 @@ public final class MainVideoPlayer extends Activity {
@Override
public void onClick(View v) {
super.onClick(v);
if (v.getId() == repeatButton.getId()) {
onRepeatClicked();
} else if (v.getId() == playPauseButton.getId()) {
if (v.getId() == playPauseButton.getId()) {
onVideoPlayPause();
} else if (v.getId() == playPreviousButton.getId()) {
@@ -354,6 +363,12 @@ public final class MainVideoPlayer extends Activity {
} else if (v.getId() == queueButton.getId()) {
onQueueClicked();
return;
} else if (v.getId() == repeatButton.getId()) {
onRepeatClicked();
return;
} else if (v.getId() == shuffleButton.getId()) {
onShuffleClicked();
return;
}
if (getCurrentState() != STATE_COMPLETED) {
@@ -371,10 +386,14 @@ public final class MainVideoPlayer extends Activity {
private void onQueueClicked() {
queueVisible = true;
buildQueue();
hideSystemUi();
buildQueue();
updatePlaybackButtons();
getControlsRoot().setVisibility(View.INVISIBLE);
queueLayout.setVisibility(View.VISIBLE);
itemsList.smoothScrollToPosition(playQueue.getIndex());
}
@@ -527,12 +546,20 @@ public final class MainVideoPlayer extends Activity {
}, delay);
}
private void updatePlaybackButtons() {
if (repeatButton == null || shuffleButton == null ||
simpleExoPlayer == null || playQueue == null) return;
setRepeatModeButton(repeatButton, simpleExoPlayer.getRepeatMode());
setShuffleButton(shuffleButton, playQueue.isShuffled());
}
private void buildQueue() {
queueLayout = findViewById(R.id.play_queue_control);
queueLayout = findViewById(R.id.playQueuePanel);
itemsListCloseButton = findViewById(R.id.play_queue_close_area);
itemsListCloseButton = findViewById(R.id.playQueueClose);
itemsList = findViewById(R.id.play_queue);
itemsList = findViewById(R.id.playQueue);
itemsList.setAdapter(playQueueAdapter);
itemsList.setClickable(true);
itemsList.setLongClickable(true);

View File

@@ -405,12 +405,6 @@ public final class PopupVideoPlayer extends Service {
resizingIndicator = rootView.findViewById(R.id.resizing_indicator);
}
@Override
public int getPreferredResolution() {
if (sharedPreferences == null || context == null) return Integer.MAX_VALUE;
return Localization.resolutionOf(sharedPreferences.getString(context.getString(R.string.default_popup_resolution_key), context.getString(R.string.default_popup_resolution_value)));
}
@Override
public void destroy() {
super.destroy();
@@ -443,8 +437,9 @@ public final class PopupVideoPlayer extends Service {
context,
MainVideoPlayer.class,
this.getPlayQueue(),
this.getCurrentResolutionTarget(),
this.getPlaybackSpeed()
this.simpleExoPlayer.getRepeatMode(),
this.getPlaybackSpeed(),
this.getPlaybackPitch()
);
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

View File

@@ -115,6 +115,11 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
bind();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_play_queue, menu);
@@ -164,7 +169,6 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
serviceBound = false;
stopPlayerListener();
player = null;
finish();
}
}
@@ -181,6 +185,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
player = playerFrom(service);
if (player == null || player.playQueue == null || player.playQueueAdapter == null || player.simpleExoPlayer == null) {
unbind();
finish();
} else {
buildComponents();
startPlayerListener();
@@ -460,6 +465,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
@Override
public void onServiceStopped() {
unbind();
finish();
}
////////////////////////////////////////////////////////////////////////////

View File

@@ -25,7 +25,6 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
@@ -47,18 +46,10 @@ import android.widget.SeekBar;
import android.widget.TextView;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
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.MergingMediaSource;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.FixedTrackSelection;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import org.schabi.newpipe.R;
@@ -90,7 +81,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
//////////////////////////////////////////////////////////////////////////*/
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
public static final String MAX_RESOLUTION = "max_resolution";
private ArrayList<VideoStream> availableStreams;
private int selectedStreamIndex;
@@ -101,11 +91,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public static final int DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds
private static final TrackSelection.Factory FIXED_FACTORY = new FixedTrackSelection.Factory();
private List<TrackGroupInfo> trackGroupInfos;
private TrackGroupArray videoTrackGroups;
private TrackGroup selectedVideoTrackGroup;
private boolean startedFromNewPipe = true;
protected boolean wasPlaying = false;
@@ -130,7 +115,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
private SeekBar playbackSeekBar;
private TextView playbackCurrentTime;
private TextView playbackEndTime;
private TextView playbackSpeed;
private TextView playbackSpeedTextView;
private View topControlsRoot;
private TextView qualityTextView;
@@ -173,7 +158,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
this.playbackSeekBar = rootView.findViewById(R.id.playbackSeekBar);
this.playbackCurrentTime = rootView.findViewById(R.id.playbackCurrentTime);
this.playbackEndTime = rootView.findViewById(R.id.playbackEndTime);
this.playbackSpeed = rootView.findViewById(R.id.playbackSpeed);
this.playbackSpeedTextView = rootView.findViewById(R.id.playbackSpeed);
this.bottomControlsRoot = rootView.findViewById(R.id.bottomControls);
this.topControlsRoot = rootView.findViewById(R.id.topControls);
this.qualityTextView = rootView.findViewById(R.id.qualityTextView);
@@ -186,7 +171,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
this.playbackSeekBar.getProgressDrawable().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
this.qualityPopupMenu = new PopupMenu(context, qualityTextView);
this.playbackSpeedPopupMenu = new PopupMenu(context, playbackSpeed);
this.playbackSpeedPopupMenu = new PopupMenu(context, playbackSpeedTextView);
((ProgressBar) this.loadingPanel.findViewById(R.id.progressBarLoadingPanel)).getIndeterminateDrawable().setColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY);
@@ -196,7 +181,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public void initListeners() {
super.initListeners();
playbackSeekBar.setOnSeekBarChangeListener(this);
playbackSpeed.setOnClickListener(this);
playbackSpeedTextView.setOnClickListener(this);
fullScreenButton.setOnClickListener(this);
qualityTextView.setOnClickListener(this);
}
@@ -212,78 +197,21 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
}
}
@Override
protected void postProcess(@NonNull final Intent intent) {
final int resolutionTarget = intent.getIntExtra(MAX_RESOLUTION, getPreferredResolution());
trackSelector.setParameters(
// Assume video is horizontal
new DefaultTrackSelector.Parameters().withMaxVideoSize(Integer.MAX_VALUE, resolutionTarget)
);
}
public abstract int getPreferredResolution();
/*//////////////////////////////////////////////////////////////////////////
// UI Builders
//////////////////////////////////////////////////////////////////////////*/
private final class TrackGroupInfo {
final int track;
final int group;
final Format format;
TrackGroupInfo(final int track, final int group, final Format format) {
this.track = track;
this.group = group;
this.format = format;
}
}
private void buildQualityMenu() {
if (qualityPopupMenu == null || videoTrackGroups == null || selectedVideoTrackGroup == null
|| availableStreams == null || videoTrackGroups.length != availableStreams.size()) return;
public void buildQualityMenu() {
if (qualityPopupMenu == null) return;
qualityPopupMenu.getMenu().removeGroup(qualityPopupMenuGroupId);
trackGroupInfos = new ArrayList<>();
int acc = 0;
// Each group represent a source in sorted order of how the media source was built
for (int groupIndex = 0; groupIndex < videoTrackGroups.length; groupIndex++) {
final TrackGroup group = videoTrackGroups.get(groupIndex);
final VideoStream stream = availableStreams.get(groupIndex);
// For each source, there may be one or multiple tracks depending on the source type
for (int trackIndex = 0; trackIndex < group.length; trackIndex++) {
final Format format = group.getFormat(trackIndex);
final boolean isSetCurrent = selectedVideoTrackGroup.indexOf(format) != -1;
if (group.length == 1 && videoTrackGroups.length == availableStreams.size()) {
// If the source is non-adaptive (extractor source), then we use the resolution contained in the stream
if (isSetCurrent) qualityTextView.setText(stream.resolution);
final String menuItem = MediaFormat.getNameById(stream.format) + " " +
stream.resolution + " (" + format.width + "x" + format.height + ")";
qualityPopupMenu.getMenu().add(qualityPopupMenuGroupId, acc, Menu.NONE, menuItem);
} else {
// Otherwise, we have an adaptive source, which contains multiple formats and
// thus have no inherent quality format
if (isSetCurrent) qualityTextView.setText(resolutionStringOf(format));
final MediaFormat mediaFormat = MediaFormat.getFromMimeType(format.sampleMimeType);
final String mediaName = mediaFormat == null ? format.sampleMimeType : mediaFormat.name;
final String menuItem = mediaName + " " + format.width + "x" + format.height;
qualityPopupMenu.getMenu().add(qualityPopupMenuGroupId, acc, Menu.NONE, menuItem);
}
trackGroupInfos.add(new TrackGroupInfo(trackIndex, groupIndex, format));
acc++;
}
for (int i = 0; i < availableStreams.size(); i++) {
VideoStream videoStream = availableStreams.get(i);
qualityPopupMenu.getMenu().add(qualityPopupMenuGroupId, i, Menu.NONE, MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
}
qualityTextView.setText(getSelectedVideoStream().resolution);
qualityPopupMenu.setOnMenuItemClickListener(this);
qualityPopupMenu.setOnDismissListener(this);
qualityTextView.setVisibility(View.VISIBLE);
}
private void buildPlaybackSpeedMenu() {
@@ -293,7 +221,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
for (int i = 0; i < PLAYBACK_SPEEDS.length; i++) {
playbackSpeedPopupMenu.getMenu().add(playbackSpeedPopupMenuGroupId, i, Menu.NONE, formatSpeed(PLAYBACK_SPEEDS[i]));
}
playbackSpeed.setText(formatSpeed(getPlaybackSpeed()));
playbackSpeedTextView.setText(formatSpeed(getPlaybackSpeed()));
playbackSpeedPopupMenu.setOnMenuItemClickListener(this);
playbackSpeedPopupMenu.setOnDismissListener(this);
}
@@ -305,27 +233,46 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
@Override
public void sync(@NonNull final PlayQueueItem item, @Nullable final StreamInfo info) {
super.sync(item, info);
qualityTextView.setVisibility(View.GONE);
playbackSpeedTextView.setVisibility(View.GONE);
if (info != null) {
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
availableStreams = new ArrayList<>(videos);
selectedStreamIndex = ListHelper.getDefaultResolutionIndex(context, videos);
}
final int qualityIndex = item.getQualityIndex();
if (qualityIndex == PlayQueueItem.DEFAULT_QUALITY) {
selectedStreamIndex = ListHelper.getDefaultResolutionIndex(context, videos);
} else {
selectedStreamIndex = qualityIndex;
}
buildPlaybackSpeedMenu();
buildQualityMenu();
buildQualityMenu();
buildPlaybackSpeedMenu();
qualityTextView.setVisibility(View.VISIBLE);
playbackSpeedTextView.setVisibility(View.VISIBLE);
}
}
public MediaSource sourceOf(final StreamInfo info) {
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
final List<VideoStream> videos = ListHelper.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false);
List<MediaSource> sources = new ArrayList<>();
final int sortedStreamsIndex = item.getQualityIndex();
if (videos.isEmpty() || sortedStreamsIndex >= videos.size()) return null;
for (final VideoStream video : videos) {
final MediaSource mediaSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
sources.add(mediaSource);
final VideoStream video;
if (sortedStreamsIndex == PlayQueueItem.DEFAULT_QUALITY) {
final int index = ListHelper.getDefaultResolutionIndex(context, videos);
video = videos.get(index);
} else {
video = videos.get(sortedStreamsIndex);
}
return new MergingMediaSource(sources.toArray(new MediaSource[sources.size()]));
final MediaSource streamSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
if (!video.isVideoOnly || audio == null) return streamSource;
// Merge with audio stream in case if video does not contain audio
final MediaSource audioSource = buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
return new MergingMediaSource(streamSource, audioSource);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -403,24 +350,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
super.onTracksChanged(trackGroups, trackSelections);
if (trackSelector.getCurrentMappedTrackInfo() == null) return;
qualityTextView.setVisibility(View.GONE);
final int videoRendererIndex = getVideoRendererIndex();
if (videoRendererIndex == -1) return;
videoTrackGroups = trackSelector.getCurrentMappedTrackInfo().getTrackGroups(videoRendererIndex);
final TrackSelection trackSelection = trackSelections.get(videoRendererIndex);
if (trackSelection != null) {
selectedVideoTrackGroup = trackSelection.getTrackGroup();
buildQualityMenu();
}
}
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
if (DEBUG) {
@@ -444,7 +373,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
playbackSeekBar.setMax((int) simpleExoPlayer.getDuration());
playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration()));
playbackSpeed.setText(formatSpeed(getPlaybackSpeed()));
playbackSpeedTextView.setText(formatSpeed(getPlaybackSpeed()));
super.onPrepared(playWhenReady);
}
@@ -510,7 +439,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
onFullScreenButtonClicked();
} else if (v.getId() == qualityTextView.getId()) {
onQualitySelectorClicked();
} else if (v.getId() == playbackSpeed.getId()) {
} else if (v.getId() == playbackSpeedTextView.getId()) {
onPlaybackSpeedClicked();
}
}
@@ -524,34 +453,19 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
Log.d(TAG, "onMenuItemClick() called with: menuItem = [" + menuItem + "], menuItem.getItemId = [" + menuItem.getItemId() + "]");
if (qualityPopupMenuGroupId == menuItem.getGroupId()) {
final int itemId = menuItem.getItemId();
final TrackGroupInfo info = trackGroupInfos.get(itemId);
if (selectedStreamIndex == menuItem.getItemId()) return true;
// Set selected quality as player lifecycle persistent parameters
DefaultTrackSelector.Parameters parameters;
if (info.format.width > info.format.height) {
// Check if video horizontal
parameters = new DefaultTrackSelector.Parameters().withMaxVideoSize(Integer.MAX_VALUE, info.format.height);
} else {
// Or if vertical
parameters = new DefaultTrackSelector.Parameters().withMaxVideoSize(info.format.width, Integer.MAX_VALUE);
}
trackSelector.setParameters(parameters);
final int videoRendererIndex = getVideoRendererIndex();
if (videoRendererIndex != -1) {
// Override the selection with the selected quality in case of different frame rate
final MappingTrackSelector.SelectionOverride override = new MappingTrackSelector.SelectionOverride(FIXED_FACTORY, info.group, info.track);
trackSelector.setSelectionOverride(videoRendererIndex, videoTrackGroups, override);
}
setRecovery();
playQueue.setQuality(playQueue.getIndex(), menuItem.getItemId());
qualityTextView.setText(menuItem.getTitle());
return true;
} else if (playbackSpeedPopupMenuGroupId == menuItem.getGroupId()) {
int speedIndex = menuItem.getItemId();
float speed = PLAYBACK_SPEEDS[speedIndex];
setPlaybackSpeed(speed);
playbackSpeed.setText(formatSpeed(speed));
playbackSpeedTextView.setText(formatSpeed(speed));
}
return false;
@@ -564,6 +478,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
public void onDismiss(PopupMenu menu) {
if (DEBUG) Log.d(TAG, "onDismiss() called with: menu = [" + menu + "]");
isSomePopupMenuVisible = false;
qualityTextView.setText(getSelectedVideoStream().resolution);
}
public void onQualitySelectorClicked() {
@@ -572,6 +487,8 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
isSomePopupMenuVisible = true;
showControls(300);
VideoStream videoStream = getSelectedVideoStream();
qualityTextView.setText(MediaFormat.getNameById(videoStream.format) + " " + videoStream.resolution);
wasPlaying = simpleExoPlayer.getPlayWhenReady();
}
@@ -635,11 +552,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
return -1;
}
public String resolutionStringOf(final Format format) {
final String frameRate = format.frameRate > 0 ? String.valueOf((int) format.frameRate) : "";
return Math.min(format.width, format.height) + "p" + frameRate;
}
public boolean isControlsVisible() {
return controlsRoot != null && controlsRoot.getVisibility() == View.VISIBLE;
}
@@ -746,10 +658,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
return wasPlaying;
}
public int getQualityPopupMenuGroupId() {
return qualityPopupMenuGroupId;
}
public VideoStream getSelectedVideoStream() {
return availableStreams.get(selectedStreamIndex);
}

View File

@@ -51,7 +51,7 @@ public final class DeferredMediaSource implements MediaSource {
* Player-specific {@link com.google.android.exoplayer2.source.MediaSource} resolution
* from a given StreamInfo.
* */
MediaSource sourceOf(final StreamInfo info);
MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info);
}
private PlayQueueItem stream;
@@ -102,8 +102,8 @@ public final class DeferredMediaSource implements MediaSource {
* called once only.
*
* If loading fails here, an error will be propagated out and result in an
* {@link com.google.android.exoplayer2.ExoPlaybackException ExoPlaybackException}, which is delegated
* to the player.
* {@link com.google.android.exoplayer2.ExoPlaybackException ExoPlaybackException},
* which is delegated to the player.
* */
public synchronized void load() {
if (stream == null) {
@@ -117,7 +117,7 @@ public final class DeferredMediaSource implements MediaSource {
final Function<StreamInfo, MediaSource> onReceive = new Function<StreamInfo, MediaSource>() {
@Override
public MediaSource apply(StreamInfo streamInfo) throws Exception {
return onStreamInfoReceived(streamInfo);
return onStreamInfoReceived(stream, streamInfo);
}
};
@@ -142,17 +142,18 @@ public final class DeferredMediaSource implements MediaSource {
.subscribe(onSuccess, onError);
}
private MediaSource onStreamInfoReceived(final StreamInfo streamInfo) throws Exception {
private MediaSource onStreamInfoReceived(@NonNull final PlayQueueItem item,
@NonNull final StreamInfo info) throws Exception {
if (callback == null) {
throw new Exception("No available callback for resolving stream info.");
}
final MediaSource mediaSource = callback.sourceOf(streamInfo);
final MediaSource mediaSource = callback.sourceOf(item, info);
if (mediaSource == null) {
throw new Exception("Unable to resolve source from stream info. URL: " + stream.getUrl() +
", audio count: " + streamInfo.audio_streams.size() +
", video count: " + streamInfo.video_only_streams.size() + streamInfo.video_streams.size());
", audio count: " + info.audio_streams.size() +
", video count: " + info.video_only_streams.size() + info.video_streams.size());
}
return mediaSource;

View File

@@ -13,7 +13,7 @@ import org.schabi.newpipe.player.mediasource.DeferredMediaSource;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.events.MoveEvent;
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
import org.schabi.newpipe.playlist.events.PlayQueueEvent;
import org.schabi.newpipe.playlist.events.RemoveEvent;
import java.util.ArrayList;
@@ -65,8 +65,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
//////////////////////////////////////////////////////////////////////////*/
@Override
public MediaSource sourceOf(StreamInfo info) {
return playbackListener.sourceOf(info);
public MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info) {
return playbackListener.sourceOf(item, info);
}
/*//////////////////////////////////////////////////////////////////////////
@@ -83,8 +83,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
playQueueReactor = null;
syncReactor = null;
sources = null;
playbackListener = null;
playQueue = null;
}
/**
@@ -130,8 +128,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
// Event Reactor
//////////////////////////////////////////////////////////////////////////*/
private Subscriber<PlayQueueMessage> getReactor() {
return new Subscriber<PlayQueueMessage>() {
private Subscriber<PlayQueueEvent> getReactor() {
return new Subscriber<PlayQueueEvent>() {
@Override
public void onSubscribe(@NonNull Subscription d) {
if (playQueueReactor != null) playQueueReactor.cancel();
@@ -140,7 +138,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
}
@Override
public void onNext(@NonNull PlayQueueMessage playQueueMessage) {
public void onNext(@NonNull PlayQueueEvent playQueueMessage) {
onPlayQueueChanged(playQueueMessage);
}
@@ -152,7 +150,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
};
}
private void onPlayQueueChanged(final PlayQueueMessage event) {
private void onPlayQueueChanged(final PlayQueueEvent event) {
if (playQueue.isEmpty()) {
playbackListener.shutdown();
}
@@ -160,6 +158,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
// why no pattern matching in Java =(
switch (event.type()) {
case INIT:
case QUALITY:
case REORDER:
reset();
break;
@@ -179,6 +178,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
move(moveEvent.getFromIndex(), moveEvent.getToIndex());
break;
case ERROR:
case RECOVERY:
default:
break;
}

View File

@@ -43,7 +43,7 @@ public interface PlaybackListener {
*
* May be called at any time.
* */
MediaSource sourceOf(final StreamInfo info);
MediaSource sourceOf(final PlayQueueItem item, final StreamInfo info);
/**
* Called when the play queue can no longer to played or used.

View File

@@ -9,10 +9,12 @@ import org.schabi.newpipe.playlist.events.AppendEvent;
import org.schabi.newpipe.playlist.events.ErrorEvent;
import org.schabi.newpipe.playlist.events.InitEvent;
import org.schabi.newpipe.playlist.events.MoveEvent;
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
import org.schabi.newpipe.playlist.events.PlayQueueEvent;
import org.schabi.newpipe.playlist.events.RecoveryEvent;
import org.schabi.newpipe.playlist.events.RemoveEvent;
import org.schabi.newpipe.playlist.events.ReorderEvent;
import org.schabi.newpipe.playlist.events.SelectEvent;
import org.schabi.newpipe.playlist.events.QualityEvent;
import java.io.Serializable;
import java.util.ArrayList;
@@ -46,8 +48,8 @@ public abstract class PlayQueue implements Serializable {
private ArrayList<PlayQueueItem> streams;
private final AtomicInteger queueIndex;
private transient BehaviorSubject<PlayQueueMessage> eventBroadcast;
private transient Flowable<PlayQueueMessage> broadcastReceiver;
private transient BehaviorSubject<PlayQueueEvent> eventBroadcast;
private transient Flowable<PlayQueueEvent> broadcastReceiver;
private transient Subscription reportingReactor;
PlayQueue(final int index, final List<PlayQueueItem> startWith) {
@@ -171,7 +173,7 @@ public abstract class PlayQueue implements Serializable {
* May be null if the play queue message bus is not initialized.
* */
@NonNull
public Flowable<PlayQueueMessage> getBroadcastReceiver() {
public Flowable<PlayQueueEvent> getBroadcastReceiver() {
return broadcastReceiver;
}
@@ -273,6 +275,15 @@ public abstract class PlayQueue implements Serializable {
streams.remove(index);
}
/**
* Moves a queue item at the source index to the target index.
*
* If the item being moved is the currently playing, then the current playing index is set
* to that of the target.
* If the moved item is not the currently playing and moves to an index <b>AFTER</b> the
* current playing index, then the current playing index is decremented.
* Vice versa if the an item after the currently playing is moved <b>BEFORE</b>.
* */
public synchronized void move(final int source, final int target) {
if (source < 0 || target < 0) return;
if (source >= streams.size() || target >= streams.size()) return;
@@ -290,6 +301,42 @@ public abstract class PlayQueue implements Serializable {
broadcast(new MoveEvent(source, target));
}
/**
* Updates the quality index at the given item index.
*
* Broadcasts an update event, signalling to all recipients that they should reset.
* */
public synchronized void setQuality(final int queueIndex, final int qualityIndex) {
if (queueIndex < 0 || queueIndex >= streams.size()) return;
final PlayQueueItem item = streams.get(queueIndex);
final int oldQualityIndex = item.getQualityIndex();
item.setQualityIndex(qualityIndex);
broadcast(new QualityEvent(queueIndex, oldQualityIndex, qualityIndex));
}
/**
* Sets the recovery record of the item at the index.
*
* Broadcasts a recovery event.
* */
public synchronized void setRecovery(final int index, final long position) {
if (index < 0 || index >= streams.size()) return;
streams.get(index).setRecoveryPosition(position);
broadcast(new RecoveryEvent(index, position));
}
/**
* Revoke the recovery record of the item at the index.
*
* Broadcasts a recovery event.
* */
public synchronized void unsetRecovery(final int index) {
setRecovery(index, PlayQueueItem.RECOVERY_UNSET);
}
/**
* Shuffles the current play queue.
*
@@ -345,14 +392,14 @@ public abstract class PlayQueue implements Serializable {
// Rx Broadcast
//////////////////////////////////////////////////////////////////////////*/
private void broadcast(final PlayQueueMessage event) {
private void broadcast(final PlayQueueEvent event) {
if (eventBroadcast != null) {
eventBroadcast.onNext(event);
}
}
private Subscriber<PlayQueueMessage> getSelfReporter() {
return new Subscriber<PlayQueueMessage>() {
private Subscriber<PlayQueueEvent> getSelfReporter() {
return new Subscriber<PlayQueueEvent>() {
@Override
public void onSubscribe(Subscription s) {
if (reportingReactor != null) reportingReactor.cancel();
@@ -361,7 +408,7 @@ public abstract class PlayQueue implements Serializable {
}
@Override
public void onNext(PlayQueueMessage event) {
public void onNext(PlayQueueEvent event) {
Log.d(TAG, "Received broadcast: " + event.type().name() + ". Current index: " + getIndex() + ", play queue length: " + size() + ".");
reportingReactor.request(1);
}

View File

@@ -10,7 +10,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.playlist.events.AppendEvent;
import org.schabi.newpipe.playlist.events.ErrorEvent;
import org.schabi.newpipe.playlist.events.MoveEvent;
import org.schabi.newpipe.playlist.events.PlayQueueMessage;
import org.schabi.newpipe.playlist.events.PlayQueueEvent;
import org.schabi.newpipe.playlist.events.RemoveEvent;
import org.schabi.newpipe.playlist.events.SelectEvent;
@@ -73,7 +73,7 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
}
private void startReactor() {
final Observer<PlayQueueMessage> observer = new Observer<PlayQueueMessage>() {
final Observer<PlayQueueEvent> observer = new Observer<PlayQueueEvent>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
if (playQueueReactor != null) playQueueReactor.dispose();
@@ -81,7 +81,7 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
}
@Override
public void onNext(@NonNull PlayQueueMessage playQueueMessage) {
public void onNext(@NonNull PlayQueueEvent playQueueMessage) {
onPlayQueueChanged(playQueueMessage);
}
@@ -99,8 +99,12 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
.subscribe(observer);
}
private void onPlayQueueChanged(final PlayQueueMessage message) {
private void onPlayQueueChanged(final PlayQueueEvent message) {
switch (message.type()) {
case RECOVERY:
case QUALITY:
// Do nothing.
break;
case SELECT:
final SelectEvent selectEvent = (SelectEvent) message;
notifyItemChanged(selectEvent.getOldIndex());

View File

@@ -36,6 +36,11 @@ public class PlayQueueItem implements Serializable {
this.stream = Single.just(info);
}
PlayQueueItem(@NonNull final StreamInfo info, final int qualityIndex) {
this(info);
this.qualityIndex = qualityIndex;
}
PlayQueueItem(@NonNull final StreamInfoItem item) {
this(item.name, item.url, item.service_id, item.duration, item.thumbnail_url, item.uploader_name);
}
@@ -49,8 +54,8 @@ public class PlayQueueItem implements Serializable {
this.thumbnailUrl = thumbnailUrl;
this.uploader = uploader;
resetQualityIndex();
resetRecoveryPosition();
this.qualityIndex = DEFAULT_QUALITY;
this.recoveryPosition = RECOVERY_UNSET;
}
@NonNull
@@ -71,14 +76,24 @@ public class PlayQueueItem implements Serializable {
return duration;
}
@NonNull
public String getThumbnailUrl() {
return thumbnailUrl;
}
@NonNull
public String getUploader() {
return uploader;
}
public int getQualityIndex() {
return qualityIndex;
}
public long getRecoveryPosition() {
return recoveryPosition;
}
@Nullable
public Throwable getError() {
return error;
@@ -105,30 +120,14 @@ public class PlayQueueItem implements Serializable {
}
////////////////////////////////////////////////////////////////////////////
// Item States
// Item States, keep external access out
////////////////////////////////////////////////////////////////////////////
public int getQualityIndex() {
return qualityIndex;
}
public long getRecoveryPosition() {
return recoveryPosition;
}
public void setQualityIndex(int qualityIndex) {
/*package-private*/ void setQualityIndex(final int qualityIndex) {
this.qualityIndex = qualityIndex;
}
public void setRecoveryPosition(long recoveryPosition) {
/*package-private*/ void setRecoveryPosition(final long recoveryPosition) {
this.recoveryPosition = recoveryPosition;
}
public void resetQualityIndex() {
this.qualityIndex = DEFAULT_QUALITY;
}
public void resetRecoveryPosition() {
this.recoveryPosition = RECOVERY_UNSET;
}
}

View File

@@ -9,6 +9,10 @@ public final class SinglePlayQueue extends PlayQueue {
super(0, Collections.singletonList(new PlayQueueItem(info)));
}
public SinglePlayQueue(final StreamInfo info, final int qualityIndex) {
super(0, Collections.singletonList(new PlayQueueItem(info, qualityIndex)));
}
@Override
public boolean isComplete() {
return true;

View File

@@ -1,12 +1,12 @@
package org.schabi.newpipe.playlist.events;
public class AppendEvent implements PlayQueueMessage {
public class AppendEvent implements PlayQueueEvent {
final private int amount;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.APPEND;
public PlayQueueEventType type() {
return PlayQueueEventType.APPEND;
}
public AppendEvent(final int amount) {

View File

@@ -1,12 +1,12 @@
package org.schabi.newpipe.playlist.events;
public class ErrorEvent implements PlayQueueMessage {
public class ErrorEvent implements PlayQueueEvent {
final private int index;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.ERROR;
public PlayQueueEventType type() {
return PlayQueueEventType.ERROR;
}
public ErrorEvent(final int index) {

View File

@@ -1,8 +1,8 @@
package org.schabi.newpipe.playlist.events;
public class InitEvent implements PlayQueueMessage {
public class InitEvent implements PlayQueueEvent {
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.INIT;
public PlayQueueEventType type() {
return PlayQueueEventType.INIT;
}
}

View File

@@ -1,12 +1,12 @@
package org.schabi.newpipe.playlist.events;
public class MoveEvent implements PlayQueueMessage {
public class MoveEvent implements PlayQueueEvent {
final private int fromIndex;
final private int toIndex;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.MOVE;
public PlayQueueEventType type() {
return PlayQueueEventType.MOVE;
}
public MoveEvent(final int oldIndex, final int newIndex) {

View File

@@ -1,24 +1,7 @@
package org.schabi.newpipe.playlist.events;
public enum PlayQueueEvent {
INIT,
import java.io.Serializable;
// sent when the index is changed
SELECT,
// sent when more streams are added to the play queue
APPEND,
// sent when a pending stream is removed from the play queue
REMOVE,
// sent when two streams swap place in the play queue
MOVE,
// sent when queue is shuffled
REORDER,
// sent when the item at index has caused an exception
ERROR
public interface PlayQueueEvent extends Serializable {
PlayQueueEventType type();
}

View File

@@ -0,0 +1,30 @@
package org.schabi.newpipe.playlist.events;
public enum PlayQueueEventType {
INIT,
// sent when the index is changed
SELECT,
// sent when more streams are added to the play queue
APPEND,
// sent when a pending stream is removed from the play queue
REMOVE,
// sent when two streams swap place in the play queue
MOVE,
// sent when queue is shuffled
REORDER,
// sent when quality index is set on a stream
QUALITY,
// sent when recovery record is set on a stream
RECOVERY,
// sent when the item at index has caused an exception
ERROR
}

View File

@@ -1,7 +0,0 @@
package org.schabi.newpipe.playlist.events;
import java.io.Serializable;
public interface PlayQueueMessage extends Serializable {
PlayQueueEvent type();
}

View File

@@ -0,0 +1,31 @@
package org.schabi.newpipe.playlist.events;
public class QualityEvent implements PlayQueueEvent {
final private int streamIndex;
final private int oldQualityIndex;
final private int newQualityIndex;
@Override
public PlayQueueEventType type() {
return PlayQueueEventType.QUALITY;
}
public QualityEvent(final int streamIndex, final int oldQualityIndex, final int newQualityIndex) {
this.streamIndex = streamIndex;
this.oldQualityIndex = oldQualityIndex;
this.newQualityIndex = newQualityIndex;
}
public int getStreamIndex() {
return streamIndex;
}
public int getOldQualityIndex() {
return oldQualityIndex;
}
public int getNewQualityIndex() {
return newQualityIndex;
}
}

View File

@@ -0,0 +1,25 @@
package org.schabi.newpipe.playlist.events;
public class RecoveryEvent implements PlayQueueEvent {
final private int index;
final private long position;
@Override
public PlayQueueEventType type() {
return PlayQueueEventType.RECOVERY;
}
public RecoveryEvent(final int index, final long position) {
this.index = index;
this.position = position;
}
public int getIndex() {
return index;
}
public long getPosition() {
return position;
}
}

View File

@@ -1,12 +1,12 @@
package org.schabi.newpipe.playlist.events;
public class RemoveEvent implements PlayQueueMessage {
public class RemoveEvent implements PlayQueueEvent {
final private int index;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.REMOVE;
public PlayQueueEventType type() {
return PlayQueueEventType.REMOVE;
}
public RemoveEvent(final int index) {

View File

@@ -1,9 +1,9 @@
package org.schabi.newpipe.playlist.events;
public class ReorderEvent implements PlayQueueMessage {
public class ReorderEvent implements PlayQueueEvent {
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.REORDER;
public PlayQueueEventType type() {
return PlayQueueEventType.REORDER;
}
public ReorderEvent() {

View File

@@ -1,13 +1,13 @@
package org.schabi.newpipe.playlist.events;
public class SelectEvent implements PlayQueueMessage {
public class SelectEvent implements PlayQueueEvent {
final private int oldIndex;
final private int newIndex;
@Override
public PlayQueueEvent type() {
return PlayQueueEvent.SELECT;
public PlayQueueEventType type() {
return PlayQueueEventType.SELECT;
}
public SelectEvent(final int oldIndex, final int newIndex) {

View File

@@ -152,13 +152,4 @@ public class Localization {
}
return output;
}
public static int resolutionOf(final String resolution) {
final String[] candidates = TextUtils.split(resolution, "p");
if (candidates.length > 0 && TextUtils.isDigitsOnly(candidates[0])) {
return Integer.parseInt(candidates[0]);
} else {
return Integer.MAX_VALUE;
}
}
}

View File

@@ -60,26 +60,20 @@ public class NavigationHelper {
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
final boolean isAppending) {
final int repeatMode,
final float playbackSpeed,
final float playbackPitch) {
return getPlayerIntent(context, targetClazz, playQueue)
.putExtra(BasePlayer.APPEND_ONLY, isAppending);
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)
.putExtra(BasePlayer.PLAYBACK_PITCH, playbackPitch);
}
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
final int maxResolution) {
public static Intent getPlayerEnqueueIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue) {
return getPlayerIntent(context, targetClazz, playQueue)
.putExtra(VideoPlayer.MAX_RESOLUTION, maxResolution);
}
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
final int maxResolution,
final float playbackSpeed) {
return getPlayerIntent(context, targetClazz, playQueue, maxResolution)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed);
.putExtra(BasePlayer.APPEND_ONLY, true);
}
/*//////////////////////////////////////////////////////////////////////////