-Baked recovery records into play queue items.
-Added previous and next button on main video player. -Reverted double tap to seek for popup and main video players. -Improved shuffling to use recovery record. -Changed shuffling to place current playing stream to top of queue. -Fixed exception when removing last item on queue. -Changed fast forward and rewind button to previous and next on background notification. -Changed background notification to not update when screen is off and update immediately when screen is turned back on. -Removed unused intent strings. -Changed "Append" to "Enqueue" for append text.
This commit is contained in:
parent
21d42c92e5
commit
4553850412
14 changed files with 193 additions and 276 deletions
|
@ -93,6 +93,7 @@ public final class BackgroundPlayer extends Service {
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Notification
|
// Notification
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 123789;
|
private static final int NOTIFICATION_ID = 123789;
|
||||||
private NotificationManager notificationManager;
|
private NotificationManager notificationManager;
|
||||||
private NotificationCompat.Builder notBuilder;
|
private NotificationCompat.Builder notBuilder;
|
||||||
|
@ -101,6 +102,8 @@ public final class BackgroundPlayer extends Service {
|
||||||
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
|
private final String setAlphaMethodName = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) ? "setImageAlpha" : "setAlpha";
|
||||||
private final String setImageResourceMethodName = "setImageResource";
|
private final String setImageResourceMethodName = "setImageResource";
|
||||||
|
|
||||||
|
private boolean shouldUpdateOnProgress;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Service's LifeCycle
|
// Service's LifeCycle
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -117,6 +120,7 @@ public final class BackgroundPlayer extends Service {
|
||||||
basePlayerImpl.setup();
|
basePlayerImpl.setup();
|
||||||
|
|
||||||
mBinder = new LocalBinder();
|
mBinder = new LocalBinder();
|
||||||
|
shouldUpdateOnProgress = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -166,12 +170,9 @@ public final class BackgroundPlayer extends Service {
|
||||||
|
|
||||||
private void onScreenOnOff(boolean on) {
|
private void onScreenOnOff(boolean on) {
|
||||||
if (DEBUG) Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]");
|
if (DEBUG) Log.d(TAG, "onScreenOnOff() called with: on = [" + on + "]");
|
||||||
|
shouldUpdateOnProgress = on;
|
||||||
if (on) {
|
if (on) {
|
||||||
if (basePlayerImpl.isPlaying() && !basePlayerImpl.isProgressLoopRunning()) {
|
basePlayerImpl.triggerProgressUpdate();
|
||||||
basePlayerImpl.startProgressLoop();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
basePlayerImpl.stopProgressLoop();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,14 +325,6 @@ public final class BackgroundPlayer extends Service {
|
||||||
@Override
|
@Override
|
||||||
public void onPrepared(boolean playWhenReady) {
|
public void onPrepared(boolean playWhenReady) {
|
||||||
super.onPrepared(playWhenReady);
|
super.onPrepared(playWhenReady);
|
||||||
if (simpleExoPlayer.getDuration() < 15000) {
|
|
||||||
FAST_FORWARD_REWIND_AMOUNT = 2000;
|
|
||||||
} else if (simpleExoPlayer.getDuration() > 60 * 60 * 1000) {
|
|
||||||
FAST_FORWARD_REWIND_AMOUNT = 60000;
|
|
||||||
} else {
|
|
||||||
FAST_FORWARD_REWIND_AMOUNT = 10000;
|
|
||||||
}
|
|
||||||
PROGRESS_LOOP_INTERVAL = 1000;
|
|
||||||
simpleExoPlayer.setVolume(1f);
|
simpleExoPlayer.setVolume(1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,6 +336,10 @@ public final class BackgroundPlayer extends Service {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
|
||||||
|
updateProgress(currentProgress, duration, bufferPercent);
|
||||||
|
|
||||||
|
if (!shouldUpdateOnProgress) return;
|
||||||
|
|
||||||
resetNotification();
|
resetNotification();
|
||||||
if (bigNotRemoteView != null) {
|
if (bigNotRemoteView != null) {
|
||||||
if (currentItem != null) {
|
if (currentItem != null) {
|
||||||
|
@ -361,7 +358,6 @@ public final class BackgroundPlayer extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNotification(-1);
|
updateNotification(-1);
|
||||||
updateProgress(currentProgress, duration, bufferPercent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -122,21 +122,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
// Intent
|
// Intent
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static final String INTENT_TYPE = "intent_type";
|
|
||||||
public static final String SINGLE_STREAM = "single";
|
|
||||||
public static final String EXTERNAL_PLAYLIST = "external";
|
|
||||||
public static final String INTERNAL_PLAYLIST = "internal";
|
|
||||||
|
|
||||||
public static final String VIDEO_URL = "video_url";
|
|
||||||
public static final String VIDEO_TITLE = "video_title";
|
|
||||||
public static final String VIDEO_THUMBNAIL_URL = "video_thumbnail_url";
|
|
||||||
public static final String START_POSITION = "start_position";
|
|
||||||
public static final String CHANNEL_NAME = "channel_name";
|
|
||||||
public static final String PLAYBACK_SPEED = "playback_speed";
|
public static final String PLAYBACK_SPEED = "playback_speed";
|
||||||
|
|
||||||
public static final String PLAY_QUEUE = "play_queue";
|
public static final String PLAY_QUEUE = "play_queue";
|
||||||
public static final String RESTORE_QUEUE_INDEX = "restore_queue_index";
|
|
||||||
public static final String RESTORE_WINDOW_POS = "restore_window_pos";
|
|
||||||
public static final String APPEND_ONLY = "append_only";
|
public static final String APPEND_ONLY = "append_only";
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -149,10 +136,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
protected MediaSourceManager playbackManager;
|
protected MediaSourceManager playbackManager;
|
||||||
protected PlayQueue playQueue;
|
protected PlayQueue playQueue;
|
||||||
|
|
||||||
private boolean isRecovery = false;
|
|
||||||
private int queuePos = 0;
|
|
||||||
private long videoPos = -1;
|
|
||||||
|
|
||||||
protected StreamInfo currentInfo;
|
protected StreamInfo currentInfo;
|
||||||
protected PlayQueueItem currentItem;
|
protected PlayQueueItem currentItem;
|
||||||
|
|
||||||
|
@ -160,9 +143,10 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
// Player
|
// Player
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
|
protected final static int FAST_FORWARD_REWIND_AMOUNT = 10000; // 10 Seconds
|
||||||
public int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
|
protected final static int PLAY_PREV_ACTIVATION_LIMIT = 5000; // 5 seconds
|
||||||
public static final String CACHE_FOLDER_NAME = "exoplayer";
|
protected final static int PROGRESS_LOOP_INTERVAL = 500;
|
||||||
|
protected final static String CACHE_FOLDER_NAME = "exoplayer";
|
||||||
|
|
||||||
protected SimpleExoPlayer simpleExoPlayer;
|
protected SimpleExoPlayer simpleExoPlayer;
|
||||||
protected boolean isPrepared = false;
|
protected boolean isPrepared = false;
|
||||||
|
@ -172,7 +156,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
|
protected final DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
|
||||||
protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
protected final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
|
|
||||||
protected int PROGRESS_LOOP_INTERVAL = 500;
|
|
||||||
protected Disposable progressUpdateReactor;
|
protected Disposable progressUpdateReactor;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -269,13 +252,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve playback details
|
|
||||||
if (intent.hasExtra(RESTORE_QUEUE_INDEX) && intent.hasExtra(START_POSITION)) {
|
|
||||||
setRecovery(
|
|
||||||
intent.getIntExtra(RESTORE_QUEUE_INDEX, 0),
|
|
||||||
intent.getLongExtra(START_POSITION, 0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
|
setPlaybackSpeed(intent.getFloatExtra(PLAYBACK_SPEED, getPlaybackSpeed()));
|
||||||
|
|
||||||
// Re-initialization
|
// Re-initialization
|
||||||
|
@ -579,6 +555,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
|
|
||||||
if (playQueue == null) return;
|
if (playQueue == null) return;
|
||||||
|
|
||||||
|
setRecovery();
|
||||||
if (playQueue.isShuffled()) {
|
if (playQueue.isShuffled()) {
|
||||||
playQueue.unshuffle();
|
playQueue.unshuffle();
|
||||||
} else {
|
} else {
|
||||||
|
@ -590,26 +567,31 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
// ExoPlayer Listener
|
// ExoPlayer Listener
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@Override
|
private void recover() {
|
||||||
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
|
||||||
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
|
|
||||||
|
|
||||||
final int currentSourceIndex = playQueue.getIndex();
|
final int currentSourceIndex = playQueue.getIndex();
|
||||||
|
final PlayQueueItem currentSourceItem = playQueue.getItem();
|
||||||
|
|
||||||
// Check if already playing correct window
|
// Check if already playing correct window
|
||||||
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
final boolean isCurrentWindowCorrect = simpleExoPlayer.getCurrentWindowIndex() == currentSourceIndex;
|
||||||
|
|
||||||
// Check if recovering
|
// Check if recovering
|
||||||
if (isCurrentWindowCorrect && isRecovery && queuePos == playQueue.getIndex()) {
|
if (isCurrentWindowCorrect && currentSourceItem != null &&
|
||||||
|
currentSourceItem.getRecoveryPosition() != PlayQueueItem.RECOVERY_UNSET) {
|
||||||
|
|
||||||
// todo: figure out exactly why this is the case
|
// todo: figure out exactly why this is the case
|
||||||
/* Rounding time to nearest second as certain media cannot guarantee a sub-second seek
|
/* 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 */
|
will complete and the player might get stuck in buffering state forever */
|
||||||
final long roundedPos = (videoPos / 1000) * 1000;
|
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)roundedPos));
|
||||||
simpleExoPlayer.seekTo(roundedPos);
|
simpleExoPlayer.seekTo(currentSourceItem.getRecoveryPosition());
|
||||||
isRecovery = false;
|
currentSourceItem.resetRecoveryPosition();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimelineChanged(Timeline timeline, Object manifest) {
|
||||||
|
if (DEBUG) Log.d(TAG, "onTimelineChanged(), timeline size = " + timeline.getWindowCount());
|
||||||
|
|
||||||
if (playbackManager != null) {
|
if (playbackManager != null) {
|
||||||
playbackManager.load();
|
playbackManager.load();
|
||||||
|
@ -653,6 +635,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Player.STATE_READY: //3
|
case Player.STATE_READY: //3
|
||||||
|
recover();
|
||||||
|
|
||||||
if (!isPrepared) {
|
if (!isPrepared) {
|
||||||
isPrepared = true;
|
isPrepared = true;
|
||||||
onPrepared(playWhenReady);
|
onPrepared(playWhenReady);
|
||||||
|
@ -664,8 +648,7 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
case Player.STATE_ENDED: // 4
|
case Player.STATE_ENDED: // 4
|
||||||
// Ensure the current window has actually ended
|
// Ensure the current window has actually ended
|
||||||
// since single windows that are still loading may produce an ended state
|
// since single windows that are still loading may produce an ended state
|
||||||
if (simpleExoPlayer.isCurrentWindowSeekable() &&
|
if (simpleExoPlayer.getDuration() > 0 && simpleExoPlayer.getCurrentPosition() >= simpleExoPlayer.getDuration()) {
|
||||||
simpleExoPlayer.getCurrentPosition() >= simpleExoPlayer.getDuration()) {
|
|
||||||
changeState(STATE_COMPLETED);
|
changeState(STATE_COMPLETED);
|
||||||
isPrepared = false;
|
isPrepared = false;
|
||||||
}
|
}
|
||||||
|
@ -812,11 +795,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
else audioManager.abandonAudioFocus(this);
|
else audioManager.abandonAudioFocus(this);
|
||||||
|
|
||||||
if (getCurrentState() == STATE_COMPLETED) {
|
if (getCurrentState() == STATE_COMPLETED) {
|
||||||
if (playQueue.getIndex() == 0) {
|
playQueue.setIndex(0);
|
||||||
simpleExoPlayer.seekToDefaultPosition();
|
simpleExoPlayer.seekToDefaultPosition();
|
||||||
} else {
|
|
||||||
playQueue.setIndex(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleExoPlayer.setPlayWhenReady(!isPlaying());
|
simpleExoPlayer.setPlayWhenReady(!isPlaying());
|
||||||
|
@ -877,10 +857,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
simpleExoPlayer.seekTo(progress);
|
simpleExoPlayer.seekTo(progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPlaying() {
|
|
||||||
return simpleExoPlayer.getPlaybackState() == Player.STATE_READY && simpleExoPlayer.getPlayWhenReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Utils
|
// Utils
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -921,24 +897,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
progressUpdateReactor = null;
|
progressUpdateReactor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void tryDeleteCacheFiles(Context context) {
|
|
||||||
File cacheDir = new File(context.getExternalCacheDir(), CACHE_FOLDER_NAME);
|
|
||||||
|
|
||||||
if (cacheDir.exists()) {
|
|
||||||
try {
|
|
||||||
if (cacheDir.isDirectory()) {
|
|
||||||
for (File file : cacheDir.listFiles()) {
|
|
||||||
try {
|
|
||||||
if (DEBUG) Log.d(TAG, "tryDeleteCacheFiles: " + file.getAbsolutePath() + " deleted = " + file.delete());
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void triggerProgressUpdate() {
|
public void triggerProgressUpdate() {
|
||||||
onUpdateProgress(
|
onUpdateProgress(
|
||||||
(int) simpleExoPlayer.getCurrentPosition(),
|
(int) simpleExoPlayer.getCurrentPosition(),
|
||||||
|
@ -992,10 +950,6 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
return currentState;
|
return currentState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getVideoPos() {
|
|
||||||
return videoPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVideoUrl() {
|
public String getVideoUrl() {
|
||||||
return currentItem == null ? null : currentItem.getUrl();
|
return currentItem == null ? null : currentItem.getUrl();
|
||||||
}
|
}
|
||||||
|
@ -1012,12 +966,8 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
return simpleExoPlayer != null && simpleExoPlayer.getPlaybackState() == Player.STATE_ENDED;
|
return simpleExoPlayer != null && simpleExoPlayer.getPlaybackState() == Player.STATE_ENDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPrepared() {
|
public boolean isPlaying() {
|
||||||
return isPrepared;
|
return simpleExoPlayer.getPlaybackState() == Player.STATE_READY && simpleExoPlayer.getPlayWhenReady();
|
||||||
}
|
|
||||||
|
|
||||||
public void setPrepared(boolean prepared) {
|
|
||||||
isPrepared = prepared;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getPlaybackSpeed() {
|
public float getPlaybackSpeed() {
|
||||||
|
@ -1045,18 +995,10 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch));
|
simpleExoPlayer.setPlaybackParameters(new PlaybackParameters(speed, pitch));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentQueueIndex() {
|
|
||||||
return playQueue != null ? playQueue.getIndex() : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCurrentResolutionTarget() {
|
public int getCurrentResolutionTarget() {
|
||||||
return trackSelector != null ? trackSelector.getParameters().maxVideoHeight : Integer.MAX_VALUE;
|
return trackSelector != null ? trackSelector.getParameters().maxVideoHeight : Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPlayerCurrentPosition() {
|
|
||||||
return simpleExoPlayer != null ? simpleExoPlayer.getCurrentPosition() : 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayQueue getPlayQueue() {
|
public PlayQueue getPlayQueue() {
|
||||||
return playQueue;
|
return playQueue;
|
||||||
}
|
}
|
||||||
|
@ -1069,14 +1011,19 @@ public abstract class BasePlayer implements Player.EventListener,
|
||||||
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
|
return progressUpdateReactor != null && !progressUpdateReactor.isDisposed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getRecovery() {
|
public void setRecovery() {
|
||||||
return isRecovery;
|
if (playQueue == null || simpleExoPlayer == null) return;
|
||||||
|
|
||||||
|
final int queuePos = playQueue.getIndex();
|
||||||
|
final long windowPos = simpleExoPlayer.getCurrentPosition();
|
||||||
|
|
||||||
|
setRecovery(queuePos, windowPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRecovery(final int queuePos, final long windowPos) {
|
public void setRecovery(final int queuePos, final long windowPos) {
|
||||||
|
if (playQueue.size() <= queuePos) return;
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos);
|
if (DEBUG) Log.d(TAG, "Setting recovery, queue: " + queuePos + ", pos: " + windowPos);
|
||||||
this.isRecovery = true;
|
playQueue.getItem(queuePos).setRecoveryPosition(windowPos);
|
||||||
this.queuePos = queuePos;
|
|
||||||
this.videoPos = windowPos;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,10 +126,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
|
|
||||||
if (playerImpl.getPlayer() != null) {
|
if (playerImpl.getPlayer() != null) {
|
||||||
playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady();
|
playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady();
|
||||||
playerImpl.setRecovery(
|
playerImpl.setRecovery();
|
||||||
playerImpl.getCurrentQueueIndex(),
|
|
||||||
(int) playerImpl.getPlayer().getCurrentPosition()
|
|
||||||
);
|
|
||||||
playerImpl.destroyPlayer();
|
playerImpl.destroyPlayer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +221,8 @@ public final class MainVideoPlayer extends Activity {
|
||||||
|
|
||||||
private ImageButton screenRotationButton;
|
private ImageButton screenRotationButton;
|
||||||
private ImageButton playPauseButton;
|
private ImageButton playPauseButton;
|
||||||
|
private ImageButton playPreviousButton;
|
||||||
|
private ImageButton playNextButton;
|
||||||
|
|
||||||
private RelativeLayout queueLayout;
|
private RelativeLayout queueLayout;
|
||||||
private ImageButton itemsListCloseButton;
|
private ImageButton itemsListCloseButton;
|
||||||
|
@ -248,6 +247,8 @@ public final class MainVideoPlayer extends Activity {
|
||||||
|
|
||||||
this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
|
this.screenRotationButton = rootView.findViewById(R.id.screenRotationButton);
|
||||||
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
|
this.playPauseButton = rootView.findViewById(R.id.playPauseButton);
|
||||||
|
this.playPreviousButton = rootView.findViewById(R.id.playPreviousButton);
|
||||||
|
this.playNextButton = rootView.findViewById(R.id.playNextButton);
|
||||||
|
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
}
|
}
|
||||||
|
@ -264,6 +265,8 @@ public final class MainVideoPlayer extends Activity {
|
||||||
queueButton.setOnClickListener(this);
|
queueButton.setOnClickListener(this);
|
||||||
repeatButton.setOnClickListener(this);
|
repeatButton.setOnClickListener(this);
|
||||||
playPauseButton.setOnClickListener(this);
|
playPauseButton.setOnClickListener(this);
|
||||||
|
playPreviousButton.setOnClickListener(this);
|
||||||
|
playNextButton.setOnClickListener(this);
|
||||||
screenRotationButton.setOnClickListener(this);
|
screenRotationButton.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,13 +318,12 @@ public final class MainVideoPlayer extends Activity {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRecovery();
|
||||||
final Intent intent = NavigationHelper.getPlayerIntent(
|
final Intent intent = NavigationHelper.getPlayerIntent(
|
||||||
context,
|
context,
|
||||||
PopupVideoPlayer.class,
|
PopupVideoPlayer.class,
|
||||||
this.getPlayQueue(),
|
this.getPlayQueue(),
|
||||||
this.getCurrentResolutionTarget(),
|
this.getCurrentResolutionTarget(),
|
||||||
this.getCurrentQueueIndex(),
|
|
||||||
this.getPlayerCurrentPosition(),
|
|
||||||
this.getPlaybackSpeed()
|
this.getPlaybackSpeed()
|
||||||
);
|
);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
|
@ -340,6 +342,12 @@ public final class MainVideoPlayer extends Activity {
|
||||||
} else if (v.getId() == playPauseButton.getId()) {
|
} else if (v.getId() == playPauseButton.getId()) {
|
||||||
onVideoPlayPause();
|
onVideoPlayPause();
|
||||||
|
|
||||||
|
} else if (v.getId() == playPreviousButton.getId()) {
|
||||||
|
onPlayPrevious();
|
||||||
|
|
||||||
|
} else if (v.getId() == playNextButton.getId()) {
|
||||||
|
onPlayNext();
|
||||||
|
|
||||||
} else if (v.getId() == screenRotationButton.getId()) {
|
} else if (v.getId() == screenRotationButton.getId()) {
|
||||||
onScreenRotationClicked();
|
onScreenRotationClicked();
|
||||||
|
|
||||||
|
@ -367,6 +375,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
hideSystemUi();
|
hideSystemUi();
|
||||||
getControlsRoot().setVisibility(View.INVISIBLE);
|
getControlsRoot().setVisibility(View.INVISIBLE);
|
||||||
queueLayout.setVisibility(View.VISIBLE);
|
queueLayout.setVisibility(View.VISIBLE);
|
||||||
|
itemsList.smoothScrollToPosition(playQueue.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onQueueClosed() {
|
private void onQueueClosed() {
|
||||||
|
@ -410,18 +419,24 @@ public final class MainVideoPlayer extends Activity {
|
||||||
// States
|
// States
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
private void animatePlayButtons(final boolean show, final int duration) {
|
||||||
|
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
|
||||||
|
animateView(playPreviousButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
|
||||||
|
animateView(playNextButton, AnimationUtils.Type.SCALE_AND_ALPHA, show, duration);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlocked() {
|
public void onBlocked() {
|
||||||
super.onBlocked();
|
super.onBlocked();
|
||||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100);
|
animatePlayButtons(false, 100);
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBuffering() {
|
public void onBuffering() {
|
||||||
super.onBuffering();
|
super.onBuffering();
|
||||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100);
|
animatePlayButtons(false, 100);
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +447,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
playPauseButton.setImageResource(R.drawable.ic_pause_white);
|
||||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200);
|
animatePlayButtons(true, 200);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
showSystemUi();
|
showSystemUi();
|
||||||
|
@ -446,7 +461,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
|
||||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 200);
|
animatePlayButtons(true, 200);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -457,7 +472,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public void onPausedSeek() {
|
public void onPausedSeek() {
|
||||||
super.onPausedSeek();
|
super.onPausedSeek();
|
||||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, false, 100);
|
animatePlayButtons(false, 100);
|
||||||
getRootView().setKeepScreenOn(true);
|
getRootView().setKeepScreenOn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,7 +484,7 @@ public final class MainVideoPlayer extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
playPauseButton.setImageResource(R.drawable.ic_replay_white);
|
playPauseButton.setImageResource(R.drawable.ic_replay_white);
|
||||||
animateView(playPauseButton, AnimationUtils.Type.SCALE_AND_ALPHA, true, 300);
|
animatePlayButtons(true, 300);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -619,15 +634,12 @@ public final class MainVideoPlayer extends Activity {
|
||||||
@Override
|
@Override
|
||||||
public boolean onDoubleTap(MotionEvent e) {
|
public boolean onDoubleTap(MotionEvent e) {
|
||||||
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
|
||||||
//if (!playerImpl.isPlaying()) return false;
|
if (!playerImpl.isPlaying()) return false;
|
||||||
if (!playerImpl.isPlayerReady()) return false;
|
|
||||||
|
|
||||||
if (e.getX() > playerImpl.getRootView().getWidth() / 2)
|
if (e.getX() > playerImpl.getRootView().getWidth() / 2)
|
||||||
playerImpl.onPlayNext();
|
playerImpl.onFastForward();
|
||||||
//playerImpl.onFastForward();
|
|
||||||
else
|
else
|
||||||
playerImpl.onPlayPrevious();
|
playerImpl.onFastRewind();
|
||||||
//playerImpl.onFastRewind();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -435,6 +435,8 @@ public final class PopupVideoPlayer extends Service {
|
||||||
super.onFullScreenButtonClicked();
|
super.onFullScreenButtonClicked();
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called");
|
||||||
|
|
||||||
|
playerImpl.setRecovery();
|
||||||
Intent intent;
|
Intent intent;
|
||||||
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
|
if (!getSharedPreferences().getBoolean(getResources().getString(R.string.use_old_player_key), false)) {
|
||||||
intent = NavigationHelper.getPlayerIntent(
|
intent = NavigationHelper.getPlayerIntent(
|
||||||
|
@ -442,8 +444,6 @@ public final class PopupVideoPlayer extends Service {
|
||||||
MainVideoPlayer.class,
|
MainVideoPlayer.class,
|
||||||
this.getPlayQueue(),
|
this.getPlayQueue(),
|
||||||
this.getCurrentResolutionTarget(),
|
this.getCurrentResolutionTarget(),
|
||||||
this.getCurrentQueueIndex(),
|
|
||||||
this.getPlayerCurrentPosition(),
|
|
||||||
this.getPlaybackSpeed()
|
this.getPlaybackSpeed()
|
||||||
);
|
);
|
||||||
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
if (!isStartedFromNewPipe()) intent.putExtra(VideoPlayer.STARTED_FROM_NEWPIPE, false);
|
||||||
|
@ -703,11 +703,9 @@ public final class PopupVideoPlayer extends Service {
|
||||||
if (!playerImpl.isPlaying() || !playerImpl.isPlayerReady()) return false;
|
if (!playerImpl.isPlaying() || !playerImpl.isPlayerReady()) return false;
|
||||||
|
|
||||||
if (e.getX() > popupWidth / 2) {
|
if (e.getX() > popupWidth / 2) {
|
||||||
//playerImpl.onFastForward();
|
playerImpl.onFastForward();
|
||||||
playerImpl.onPlayNext();
|
|
||||||
} else {
|
} else {
|
||||||
//playerImpl.onFastRewind();
|
playerImpl.onFastRewind();
|
||||||
playerImpl.onPlayPrevious();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -382,11 +382,9 @@ public abstract class ServicePlayerActivity extends AppCompatActivity
|
||||||
|
|
||||||
} else if (view.getId() == backwardButton.getId()) {
|
} else if (view.getId() == backwardButton.getId()) {
|
||||||
player.onPlayPrevious();
|
player.onPlayPrevious();
|
||||||
scrollToSelected();
|
|
||||||
|
|
||||||
} else if (view.getId() == playPauseButton.getId()) {
|
} else if (view.getId() == playPauseButton.getId()) {
|
||||||
player.onVideoPlayPause();
|
player.onVideoPlayPause();
|
||||||
scrollToSelected();
|
|
||||||
|
|
||||||
} else if (view.getId() == forwardButton.getId()) {
|
} else if (view.getId() == forwardButton.getId()) {
|
||||||
player.onPlayNext();
|
player.onPlayNext();
|
||||||
|
|
|
@ -89,16 +89,12 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
|
||||||
// Intent
|
// Intent
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
public static final String VIDEO_STREAMS_LIST = "video_streams_list";
|
|
||||||
public static final String VIDEO_ONLY_AUDIO_STREAM = "video_only_audio_stream";
|
|
||||||
public static final String INDEX_SEL_VIDEO_STREAM = "index_selected_video_stream";
|
|
||||||
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
public static final String STARTED_FROM_NEWPIPE = "started_from_newpipe";
|
||||||
|
|
||||||
public static final String PLAYER_INTENT = "player_intent";
|
|
||||||
public static final String MAX_RESOLUTION = "max_resolution";
|
public static final String MAX_RESOLUTION = "max_resolution";
|
||||||
|
|
||||||
private ArrayList<VideoStream> availableStreams;
|
private ArrayList<VideoStream> availableStreams;
|
||||||
private int selectedStreamIndex;
|
private int selectedStreamIndex;
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Player
|
// Player
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
|
@ -153,6 +153,10 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPlayQueueChanged(final PlayQueueMessage event) {
|
private void onPlayQueueChanged(final PlayQueueMessage event) {
|
||||||
|
if (playQueue.isEmpty()) {
|
||||||
|
playbackListener.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
// why no pattern matching in Java =(
|
// why no pattern matching in Java =(
|
||||||
switch (event.type()) {
|
switch (event.type()) {
|
||||||
case INIT:
|
case INIT:
|
||||||
|
@ -168,6 +172,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
final RemoveEvent removeEvent = (RemoveEvent) event;
|
final RemoveEvent removeEvent = (RemoveEvent) event;
|
||||||
remove(removeEvent.index());
|
remove(removeEvent.index());
|
||||||
|
sync();
|
||||||
break;
|
break;
|
||||||
case MOVE:
|
case MOVE:
|
||||||
final MoveEvent moveEvent = (MoveEvent) event;
|
final MoveEvent moveEvent = (MoveEvent) event;
|
||||||
|
@ -181,8 +186,6 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||||
if (!isPlayQueueReady()) {
|
if (!isPlayQueueReady()) {
|
||||||
tryBlock();
|
tryBlock();
|
||||||
playQueue.fetch();
|
playQueue.fetch();
|
||||||
} else if (playQueue.isEmpty()) {
|
|
||||||
playbackListener.shutdown();
|
|
||||||
} else {
|
} else {
|
||||||
load(); // All event warrants a load
|
load(); // All event warrants a load
|
||||||
}
|
}
|
||||||
|
@ -219,6 +222,7 @@ public class MediaSourceManager implements DeferredMediaSource.Callback {
|
||||||
|
|
||||||
private void sync() {
|
private void sync() {
|
||||||
final PlayQueueItem currentItem = playQueue.getItem();
|
final PlayQueueItem currentItem = playQueue.getItem();
|
||||||
|
if (currentItem == null) return;
|
||||||
|
|
||||||
final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
|
final Consumer<StreamInfo> syncPlayback = new Consumer<StreamInfo>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -50,10 +50,6 @@ public abstract class PlayQueue implements Serializable {
|
||||||
private transient Flowable<PlayQueueMessage> broadcastReceiver;
|
private transient Flowable<PlayQueueMessage> broadcastReceiver;
|
||||||
private transient Subscription reportingReactor;
|
private transient Subscription reportingReactor;
|
||||||
|
|
||||||
PlayQueue() {
|
|
||||||
this(0, Collections.<PlayQueueItem>emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayQueue(final int index, final List<PlayQueueItem> startWith) {
|
PlayQueue(final int index, final List<PlayQueueItem> startWith) {
|
||||||
streams = new ArrayList<>();
|
streams = new ArrayList<>();
|
||||||
streams.addAll(startWith);
|
streams.addAll(startWith);
|
||||||
|
@ -81,12 +77,9 @@ public abstract class PlayQueue implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispose this play queue by stopping all message buses and clearing the playlist.
|
* Dispose the play queue by stopping all message buses.
|
||||||
* */
|
* */
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (backup != null) backup.clear();
|
|
||||||
if (streams != null) streams.clear();
|
|
||||||
|
|
||||||
if (eventBroadcast != null) eventBroadcast.onComplete();
|
if (eventBroadcast != null) eventBroadcast.onComplete();
|
||||||
if (reportingReactor != null) reportingReactor.cancel();
|
if (reportingReactor != null) reportingReactor.cancel();
|
||||||
|
|
||||||
|
@ -265,11 +258,12 @@ public abstract class PlayQueue implements Serializable {
|
||||||
|
|
||||||
private synchronized void removeInternal(final int index) {
|
private synchronized void removeInternal(final int index) {
|
||||||
final int currentIndex = queueIndex.get();
|
final int currentIndex = queueIndex.get();
|
||||||
|
final int size = size();
|
||||||
|
|
||||||
if (currentIndex > index) {
|
if (currentIndex > index) {
|
||||||
queueIndex.decrementAndGet();
|
queueIndex.decrementAndGet();
|
||||||
} else if (currentIndex >= size()) {
|
} else if (currentIndex >= size) {
|
||||||
queueIndex.set(0);
|
queueIndex.set(currentIndex % (size - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backup != null) {
|
if (backup != null) {
|
||||||
|
@ -300,9 +294,8 @@ public abstract class PlayQueue implements Serializable {
|
||||||
* Shuffles the current play queue.
|
* Shuffles the current play queue.
|
||||||
*
|
*
|
||||||
* This method first backs up the existing play queue and item being played.
|
* This method first backs up the existing play queue and item being played.
|
||||||
* Then a newly shuffled play queue will be generated along with the index of
|
* Then a newly shuffled play queue will be generated along with currently
|
||||||
* the previously playing item if it is found in the shuffled play queue. If
|
* playing item placed at the beginning of the queue.
|
||||||
* not found, the current index will reset to 0.
|
|
||||||
*
|
*
|
||||||
* Will emit a {@link ReorderEvent} in any context.
|
* Will emit a {@link ReorderEvent} in any context.
|
||||||
* */
|
* */
|
||||||
|
@ -315,10 +308,9 @@ public abstract class PlayQueue implements Serializable {
|
||||||
|
|
||||||
final int newIndex = streams.indexOf(current);
|
final int newIndex = streams.indexOf(current);
|
||||||
if (newIndex != -1) {
|
if (newIndex != -1) {
|
||||||
queueIndex.set(newIndex);
|
streams.add(0, streams.remove(newIndex));
|
||||||
} else {
|
|
||||||
queueIndex.set(0);
|
|
||||||
}
|
}
|
||||||
|
queueIndex.set(0);
|
||||||
|
|
||||||
broadcast(new ReorderEvent());
|
broadcast(new ReorderEvent());
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import io.reactivex.functions.Consumer;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
public class PlayQueueItem implements Serializable {
|
public class PlayQueueItem implements Serializable {
|
||||||
|
final public static int DEFAULT_QUALITY = Integer.MIN_VALUE;
|
||||||
|
final public static long RECOVERY_UNSET = Long.MIN_VALUE;
|
||||||
|
|
||||||
final private String title;
|
final private String title;
|
||||||
final private String url;
|
final private String url;
|
||||||
|
@ -23,28 +25,32 @@ public class PlayQueueItem implements Serializable {
|
||||||
final private String thumbnailUrl;
|
final private String thumbnailUrl;
|
||||||
final private String uploader;
|
final private String uploader;
|
||||||
|
|
||||||
|
private int qualityIndex;
|
||||||
|
private long recoveryPosition;
|
||||||
private Throwable error;
|
private Throwable error;
|
||||||
|
|
||||||
private transient Single<StreamInfo> stream;
|
private transient Single<StreamInfo> stream;
|
||||||
|
|
||||||
PlayQueueItem(final StreamInfo streamInfo) {
|
PlayQueueItem(@NonNull final StreamInfo info) {
|
||||||
this.title = streamInfo.name;
|
this(info.name, info.url, info.service_id, info.duration, info.thumbnail_url, info.uploader_name);
|
||||||
this.url = streamInfo.url;
|
this.stream = Single.just(info);
|
||||||
this.serviceId = streamInfo.service_id;
|
|
||||||
this.duration = streamInfo.duration;
|
|
||||||
this.thumbnailUrl = streamInfo.thumbnail_url;
|
|
||||||
this.uploader = streamInfo.uploader_name;
|
|
||||||
|
|
||||||
this.stream = Single.just(streamInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayQueueItem(final StreamInfoItem streamInfoItem) {
|
PlayQueueItem(@NonNull final StreamInfoItem item) {
|
||||||
this.title = streamInfoItem.name;
|
this(item.name, item.url, item.service_id, item.duration, item.thumbnail_url, item.uploader_name);
|
||||||
this.url = streamInfoItem.url;
|
}
|
||||||
this.serviceId = streamInfoItem.service_id;
|
|
||||||
this.duration = streamInfoItem.duration;
|
private PlayQueueItem(final String name, final String url, final int serviceId,
|
||||||
this.thumbnailUrl = streamInfoItem.thumbnail_url;
|
final long duration, final String thumbnailUrl, final String uploader) {
|
||||||
this.uploader = streamInfoItem.uploader_name;
|
this.title = name;
|
||||||
|
this.url = url;
|
||||||
|
this.serviceId = serviceId;
|
||||||
|
this.duration = duration;
|
||||||
|
this.thumbnailUrl = thumbnailUrl;
|
||||||
|
this.uploader = uploader;
|
||||||
|
|
||||||
|
resetQualityIndex();
|
||||||
|
resetRecoveryPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@ -97,4 +103,32 @@ public class PlayQueueItem implements Serializable {
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnError(onError);
|
.doOnError(onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Item States
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public int getQualityIndex() {
|
||||||
|
return qualityIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRecoveryPosition() {
|
||||||
|
return recoveryPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQualityIndex(int qualityIndex) {
|
||||||
|
this.qualityIndex = qualityIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecoveryPosition(long recoveryPosition) {
|
||||||
|
this.recoveryPosition = recoveryPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetQualityIndex() {
|
||||||
|
this.qualityIndex = DEFAULT_QUALITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetRecoveryPosition() {
|
||||||
|
this.recoveryPosition = RECOVERY_UNSET;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,12 +77,8 @@ public class NavigationHelper {
|
||||||
final Class targetClazz,
|
final Class targetClazz,
|
||||||
final PlayQueue playQueue,
|
final PlayQueue playQueue,
|
||||||
final int maxResolution,
|
final int maxResolution,
|
||||||
final int restoringIndex,
|
|
||||||
final long startPosition,
|
|
||||||
final float playbackSpeed) {
|
final float playbackSpeed) {
|
||||||
return getPlayerIntent(context, targetClazz, playQueue, maxResolution)
|
return getPlayerIntent(context, targetClazz, playQueue, maxResolution)
|
||||||
.putExtra(VideoPlayer.RESTORE_QUEUE_INDEX, restoringIndex)
|
|
||||||
.putExtra(BasePlayer.START_POSITION, startPosition)
|
|
||||||
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed);
|
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -275,6 +275,34 @@
|
||||||
android:src="@drawable/ic_pause_white"
|
android:src="@drawable/ic_pause_white"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/playPreviousButton"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginRight="30dp"
|
||||||
|
android:layout_marginEnd="30dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:layout_toLeftOf="@id/playPauseButton"
|
||||||
|
android:layout_toStartOf="@id/playPauseButton"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:src="@drawable/exo_controls_previous"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/playNextButton"
|
||||||
|
android:layout_width="50dp"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_marginLeft="30dp"
|
||||||
|
android:layout_marginStart="30dp"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:layout_toRightOf="@id/playPauseButton"
|
||||||
|
android:layout_toEndOf="@id/playPauseButton"
|
||||||
|
android:background="#00000000"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:src="@drawable/exo_controls_next"
|
||||||
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
android:layout_height="64dp"
|
android:layout_height="64dp"
|
||||||
android:background="@color/background_notification_color"
|
android:background="@color/background_notification_color"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_repeat_white"
|
android:src="@drawable/ic_repeat_white"
|
||||||
|
@ -69,9 +71,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_action_av_fast_rewind"
|
android:src="@drawable/exo_controls_previous"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
@ -80,6 +83,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:src="@drawable/ic_pause_white"
|
android:src="@drawable/ic_pause_white"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
|
@ -89,9 +93,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_action_av_fast_forward"
|
android:src="@drawable/exo_controls_next"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
@ -101,6 +106,7 @@
|
||||||
android:layout_marginLeft="5dp"
|
android:layout_marginLeft="5dp"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="5dp"
|
android:padding="5dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_close_white_24dp"
|
android:src="@drawable/ic_close_white_24dp"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
android:layout_height="128dp"
|
android:layout_height="128dp"
|
||||||
android:background="@color/background_notification_color"
|
android:background="@color/background_notification_color"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_close_white_24dp"
|
android:src="@drawable/ic_close_white_24dp"
|
||||||
|
@ -82,9 +84,11 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="8dp"
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:layout_alignTop="@+id/notificationProgressBar"
|
android:layout_alignTop="@+id/notificationProgressBar"
|
||||||
android:layout_toRightOf="@+id/notificationCover"
|
android:layout_toRightOf="@+id/notificationCover"
|
||||||
|
android:layout_toEndOf="@+id/notificationCover"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
|
@ -109,6 +113,7 @@
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
android:src="@drawable/ic_repeat_white"
|
android:src="@drawable/ic_repeat_white"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
@ -122,9 +127,10 @@
|
||||||
android:layout_toLeftOf="@+id/notificationPlayPause"
|
android:layout_toLeftOf="@+id/notificationPlayPause"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_action_av_fast_rewind"
|
android:src="@drawable/exo_controls_previous"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
@ -137,6 +143,7 @@
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_pause_white"
|
android:src="@drawable/ic_pause_white"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
@ -150,107 +157,10 @@
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:background="#00000000"
|
android:background="#00000000"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_action_av_fast_forward"
|
android:src="@drawable/exo_controls_next"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<!--
|
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/notificationContent"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clickable="true"
|
|
||||||
android:background="@color/background_notification_color">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/notificationCover"
|
|
||||||
android:layout_width="128dp"
|
|
||||||
android:layout_height="128dp"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:src="@drawable/dummy_thumbnail"
|
|
||||||
android:scaleType="centerCrop"/>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:layout_above="@+id/notificationButtons"
|
|
||||||
android:layout_toRightOf="@+id/notificationCover"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notificationSongName"
|
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent.Title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginRight="40dp"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/notificationArtist"
|
|
||||||
style="@android:style/TextAppearance.StatusBar.EventContent"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="marquee"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:text="artist" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/playbackProgress"
|
|
||||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
|
||||||
android:layout_marginRight="8dp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/notificationStop"
|
|
||||||
android:layout_width="30dp"
|
|
||||||
android:layout_height="30dp"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:background="#00ffffff"
|
|
||||||
android:clickable="true"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:src="@drawable/ic_close_white" />
|
|
||||||
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/notificationButtons"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_alignBottom="@id/notificationCover"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:layout_toRightOf="@+id/notificationCover"
|
|
||||||
android:orientation="horizontal" >
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/notificationPlayPause"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:background="#00ffffff"
|
|
||||||
android:clickable="true"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:src="@drawable/ic_pause_white"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_centerHorizontal="true" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/notificationRewind"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:background="#00ffffff"
|
|
||||||
android:clickable="true"
|
|
||||||
android:scaleType="fitXY"
|
|
||||||
android:src="@drawable/ic_action_av_fast_rewind"
|
|
||||||
android:layout_alignParentTop="true"
|
|
||||||
android:layout_alignParentLeft="true" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>-->
|
|
||||||
|
|
|
@ -303,5 +303,5 @@
|
||||||
<string name="play_queue_remove">Remove</string>
|
<string name="play_queue_remove">Remove</string>
|
||||||
<string name="play_queue_stream_detail">Details</string>
|
<string name="play_queue_stream_detail">Details</string>
|
||||||
<string name="play_queue_audio_settings">Audio Settings</string>
|
<string name="play_queue_audio_settings">Audio Settings</string>
|
||||||
<string name="hold_to_append">Hold To Append</string>
|
<string name="hold_to_append">Hold To Enqueue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue