-Added shuffle button to background player.
-Extracted MediaSourceManager window size as parameter. -Removed redundant list manipulation in PlayQueueAdapter.
This commit is contained in:
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() {
|
||||
backup = new ArrayList<>(streams);
|
||||
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,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"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/control_backward"
|
||||
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>
|
Loading…
Reference in New Issue