From 77979eddde370befe6b36fd0562a82548b16772f Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Mon, 9 Oct 2017 18:20:11 -0700 Subject: [PATCH] -Added shuffle button to background player. -Extracted MediaSourceManager window size as parameter. -Removed redundant list manipulation in PlayQueueAdapter. --- .../newpipe/player/BackgroundPlayer.java | 9 +++-- .../player/BackgroundPlayerActivity.java | 38 +++++++++++++++---- .../org/schabi/newpipe/player/BasePlayer.java | 14 ++++++- .../player/playback/MediaSourceManager.java | 27 ++++++++----- .../schabi/newpipe/playlist/PlayQueue.java | 12 +++++- .../newpipe/playlist/PlayQueueAdapter.java | 12 ------ .../res/layout/activity_background_player.xml | 31 +++++++++++---- 7 files changed, 100 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 63637cf16..fd8687d97 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -84,7 +84,7 @@ public final class BackgroundPlayer extends Service { //////////////////////////////////////////////////////////////////////////*/ public interface PlayerEventListener { - void onPlaybackUpdate(int state, int repeatMode, PlaybackParameters parameters); + void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters); void onProgressUpdate(int currentProgress, int duration, int bufferPercent); void onMetadataUpdate(StreamInfo info); void onServiceStopped(); @@ -340,8 +340,9 @@ public final class BackgroundPlayer extends Service { } @Override - public void onRepeatClicked() { - super.onRepeatClicked(); + public void onShuffleClicked() { + super.onShuffleClicked(); + updatePlayback(); } @Override @@ -491,7 +492,7 @@ public final class BackgroundPlayer extends Service { private void updatePlayback() { if (activityListener != null) { - activityListener.onPlaybackUpdate(currentState, simpleExoPlayer.getRepeatMode(), simpleExoPlayer.getPlaybackParameters()); + activityListener.onPlaybackUpdate(currentState, simpleExoPlayer.getRepeatMode(), playQueue.isShuffled(), simpleExoPlayer.getPlaybackParameters()); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java index c90fc095d..8a5809b95 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java @@ -72,6 +72,7 @@ public class BackgroundPlayerActivity extends AppCompatActivity private ImageButton backwardButton; private ImageButton playPauseButton; private ImageButton forwardButton; + private ImageButton shuffleButton; //////////////////////////////////////////////////////////////////////////// // Activity Lifecycle @@ -196,11 +197,13 @@ public class BackgroundPlayerActivity extends AppCompatActivity backwardButton = rootView.findViewById(R.id.control_backward); playPauseButton = rootView.findViewById(R.id.control_play_pause); forwardButton = rootView.findViewById(R.id.control_forward); + shuffleButton = rootView.findViewById(R.id.control_shuffle); repeatButton.setOnClickListener(this); backwardButton.setOnClickListener(this); playPauseButton.setOnClickListener(this); forwardButton.setOnClickListener(this); + shuffleButton.setOnClickListener(this); } private void buildItemPopupMenu(final PlayQueueItem item, final View view) { @@ -298,6 +301,10 @@ public class BackgroundPlayerActivity extends AppCompatActivity context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); } + private void scrollToSelected() { + itemsList.smoothScrollToPosition(player.playQueue.getIndex()); + } + //////////////////////////////////////////////////////////////////////////// // Component On-Click Listener //////////////////////////////////////////////////////////////////////////// @@ -308,10 +315,14 @@ public class BackgroundPlayerActivity extends AppCompatActivity player.onRepeatClicked(); } else if (view.getId() == backwardButton.getId()) { player.onPlayPrevious(); + scrollToSelected(); } else if (view.getId() == playPauseButton.getId()) { player.onVideoPlayPause(); + scrollToSelected(); } else if (view.getId() == forwardButton.getId()) { player.onPlayNext(); + } else if (view.getId() == shuffleButton.getId()) { + player.onShuffleClicked(); } } @@ -340,7 +351,7 @@ public class BackgroundPlayerActivity extends AppCompatActivity //////////////////////////////////////////////////////////////////////////// @Override - public void onPlaybackUpdate(int state, int repeatMode, PlaybackParameters parameters) { + public void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters) { switch (state) { case BasePlayer.STATE_PAUSED: playPauseButton.setImageResource(R.drawable.ic_play_arrow_white); @@ -355,29 +366,41 @@ public class BackgroundPlayerActivity extends AppCompatActivity break; } - int alpha = 255; + int repeatAlpha = 255; switch (repeatMode) { case Player.REPEAT_MODE_OFF: - alpha = 77; + repeatAlpha = 77; break; case Player.REPEAT_MODE_ONE: // todo change image - alpha = 168; + repeatAlpha = 168; break; case Player.REPEAT_MODE_ALL: - alpha = 255; + repeatAlpha = 255; break; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - repeatButton.setImageAlpha(alpha); + repeatButton.setImageAlpha(repeatAlpha); } else { - repeatButton.setAlpha(alpha); + repeatButton.setAlpha(repeatAlpha); + } + + int shuffleAlpha = 255; + if (!shuffled) { + shuffleAlpha = 77; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + shuffleButton.setImageAlpha(shuffleAlpha); + } else { + shuffleButton.setAlpha(shuffleAlpha); } if (parameters != null) { final float speed = parameters.speed; final float pitch = parameters.pitch; } + + scrollToSelected(); } @Override @@ -401,6 +424,7 @@ public class BackgroundPlayerActivity extends AppCompatActivity if (info != null) { metadataTitle.setText(info.name); metadataArtist.setText(info.uploader_name); + scrollToSelected(); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index bcbfb875a..82fb10607 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -535,7 +535,7 @@ public abstract class BasePlayer implements Player.EventListener, } /*////////////////////////////////////////////////////////////////////////// - // Repeat + // Repeat and shuffle //////////////////////////////////////////////////////////////////////////*/ public void onRepeatClicked() { @@ -560,6 +560,18 @@ public abstract class BasePlayer implements Player.EventListener, if (DEBUG) Log.d(TAG, "onRepeatClicked() currentRepeatMode = " + simpleExoPlayer.getRepeatMode()); } + public void onShuffleClicked() { + if (DEBUG) Log.d(TAG, "onShuffleClicked() called"); + + if (playQueue == null) return; + + if (playQueue.isShuffled()) { + playQueue.unshuffle(); + } else { + playQueue.shuffle(); + } + } + /*////////////////////////////////////////////////////////////////////////// // ExoPlayer Listener //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index e18bed63c..8e5217c6b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -12,13 +12,11 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; 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.ErrorEvent; import org.schabi.newpipe.playlist.events.MoveEvent; import org.schabi.newpipe.playlist.events.PlayQueueMessage; import org.schabi.newpipe.playlist.events.RemoveEvent; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -29,10 +27,8 @@ import io.reactivex.functions.Consumer; public class MediaSourceManager implements DeferredMediaSource.Callback { private final String TAG = "MediaSourceManager@" + Integer.toHexString(hashCode()); // One-side rolling window size for default loading - // Effectively loads WINDOW_SIZE * 2 + 1 streams, must be greater than 0 - // todo: inject this parameter, allow user settings perhaps - private static final int WINDOW_SIZE = 1; - + // Effectively loads windowSize * 2 + 1 streams, must be greater than 0 + private final int windowSize; private PlaybackListener playbackListener; private PlayQueue playQueue; @@ -45,8 +41,15 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { public MediaSourceManager(@NonNull final PlaybackListener listener, @NonNull final PlayQueue playQueue) { + this(listener, playQueue, 1); + } + + public MediaSourceManager(@NonNull final PlaybackListener listener, + @NonNull final PlayQueue playQueue, + final int windowSize) { this.playbackListener = listener; this.playQueue = playQueue; + this.windowSize = windowSize; this.syncReactor = new SerialDisposable(); @@ -85,7 +88,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { } /** - * Loads the current playing stream and the streams within its WINDOW_SIZE bound. + * Loads the current playing stream and the streams within its windowSize bound. * * Unblocks the player once the item at the current index is loaded. * */ @@ -97,8 +100,8 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { load(currentItem); // The rest are just for seamless playback - final int leftBound = Math.max(0, currentIndex - WINDOW_SIZE); - final int rightLimit = currentIndex + WINDOW_SIZE + 1; + final int leftBound = Math.max(0, currentIndex - windowSize); + final int rightLimit = currentIndex + windowSize + 1; final int rightBound = Math.min(playQueue.size(), rightLimit); final List items = new ArrayList<>(playQueue.getStreams().subList(leftBound, rightBound)); @@ -119,6 +122,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { populateSources(); } + public int getWindowSize() { + return windowSize; + } + /*////////////////////////////////////////////////////////////////////////// // Event Reactor //////////////////////////////////////////////////////////////////////////*/ @@ -188,7 +195,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback { //////////////////////////////////////////////////////////////////////////*/ private boolean isPlayQueueReady() { - return playQueue.isComplete() || playQueue.size() - playQueue.getIndex() > WINDOW_SIZE; + return playQueue.isComplete() || playQueue.size() - playQueue.getIndex() > windowSize; } private boolean tryBlock() { diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index becebc534..4d0372be1 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -222,6 +222,8 @@ public abstract class PlayQueue implements Serializable { * */ public synchronized void append(final PlayQueueItem... items) { streams.addAll(Arrays.asList(items)); + if (backup != null) backup.addAll(Arrays.asList(items)); + broadcast(new AppendEvent(items.length)); } @@ -232,6 +234,8 @@ public abstract class PlayQueue implements Serializable { * */ public synchronized void append(final Collection items) { streams.addAll(items); + if (backup != null) backup.addAll(items); + broadcast(new AppendEvent(items.size())); } @@ -271,6 +275,10 @@ public abstract class PlayQueue implements Serializable { } streams.remove(index); + if (backup != null) { + final int backupIndex = backup.indexOf(getItem(index)); + backup.remove(backupIndex); + } } public synchronized void move(final int source, final int target) { @@ -300,7 +308,9 @@ public abstract class PlayQueue implements Serializable { * Will emit a {@link ReorderEvent} in any context. * */ public synchronized void shuffle() { - backup = new ArrayList<>(streams); + if (backup == null) { + backup = new ArrayList<>(streams); + } final PlayQueueItem current = getItem(); Collections.shuffle(streams); queueIndex.set(streams.indexOf(current)); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java index 8e33b7141..f60ca1185 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java @@ -72,18 +72,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter data) { - playQueue.append(data); - } - - public void add(final PlayQueueItem... data) { - playQueue.append(data); - } - - public void remove(final int index) { - playQueue.remove(index); - } - private void startReactor() { final Observer observer = new Observer() { @Override diff --git a/app/src/main/res/layout/activity_background_player.xml b/app/src/main/res/layout/activity_background_player.xml index fdc11acd0..17a0419dd 100644 --- a/app/src/main/res/layout/activity_background_player.xml +++ b/app/src/main/res/layout/activity_background_player.xml @@ -126,23 +126,22 @@ android:id="@+id/control_repeat" android:layout_width="25dp" android:layout_height="25dp" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true" + android:layout_toLeftOf="@+id/control_backward" android:layout_centerVertical="true" - android:layout_marginLeft="8dp" + android:layout_marginLeft="5dp" android:background="#00000000" android:clickable="true" android:focusable="true" android:scaleType="fitXY" android:src="@drawable/ic_repeat_white" - tools:ignore="ContentDescription" /> + tools:ignore="ContentDescription"/> + + \ No newline at end of file