mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 07:13:00 +00:00 
			
		
		
		
	-Added shuffle button to background player.
-Extracted MediaSourceManager window size as parameter. -Removed redundant list manipulation in PlayQueueAdapter.
This commit is contained in:
		 John Zhen M
					John Zhen M
				
			
				
					committed by
					
						 John Zhen Mo
						John Zhen Mo
					
				
			
			
				
	
			
			
			 John Zhen Mo
						John Zhen Mo
					
				
			
						parent
						
							f1e52b8b92
						
					
				
				
					commit
					77979eddde
				
			| @@ -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()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|   | ||||
| @@ -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<PlayQueueItem> 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() { | ||||
|   | ||||
| @@ -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<PlayQueueItem> 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() { | ||||
|         if (backup == null) { | ||||
|             backup = new ArrayList<>(streams); | ||||
|         } | ||||
|         final PlayQueueItem current = getItem(); | ||||
|         Collections.shuffle(streams); | ||||
|         queueIndex.set(streams.indexOf(current)); | ||||
|   | ||||
| @@ -72,18 +72,6 @@ public class PlayQueueAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | ||||
|         playQueueItemBuilder.setOnSelectedListener(listener); | ||||
|     } | ||||
|  | ||||
|     public void add(final List<PlayQueueItem> 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<PlayQueueMessage> observer = new Observer<PlayQueueMessage>() { | ||||
|             @Override | ||||
|   | ||||
| @@ -126,10 +126,9 @@ | ||||
|             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" | ||||
| @@ -142,7 +141,7 @@ | ||||
|             android:layout_width="40dp" | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:layout_marginRight="5dp" | ||||
|             android:layout_marginLeft="5dp" | ||||
|             android:layout_toLeftOf="@+id/control_play_pause" | ||||
|             android:background="#00000000" | ||||
|             android:clickable="true" | ||||
| @@ -157,8 +156,10 @@ | ||||
|             android:layout_width="50dp" | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:layout_centerHorizontal="true" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_marginLeft="5dp" | ||||
|             android:layout_marginRight="5dp" | ||||
|             android:layout_toLeftOf="@+id/control_forward" | ||||
|             android:background="#00000000" | ||||
|             android:padding="2dp" | ||||
|             android:clickable="true" | ||||
| @@ -171,9 +172,9 @@ | ||||
|             android:id="@+id/control_forward" | ||||
|             android:layout_width="40dp" | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_alignParentRight="true" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:layout_marginRight="8dp" | ||||
|             android:layout_marginRight="5dp" | ||||
|             android:layout_toRightOf="@+id/control_play_pause" | ||||
|             android:background="#00000000" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
| @@ -181,6 +182,20 @@ | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/ic_action_av_fast_forward" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/control_shuffle" | ||||
|             android:layout_width="25dp" | ||||
|             android:layout_height="25dp" | ||||
|             android:layout_toRightOf="@+id/control_forward" | ||||
|             android:layout_centerVertical="true" | ||||
|             android:layout_marginRight="5dp" | ||||
|             android:background="#00000000" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:scaleType="fitXY" | ||||
|             android:src="@drawable/ic_palette_white_24dp" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|     </RelativeLayout> | ||||
|  | ||||
| </RelativeLayout> | ||||
		Reference in New Issue
	
	Block a user