Player will be rebound when needed, prev/next/queue buttons, preserving paused state
- each time something starts to play in any player VideoDetailFragment will be started (if not yet started) and mini player will show up. It makes possible to see a playing stream in mini player even if the stream was started without using fragment or after player service was closed somehow - play/next/queue buttons will be updated in realtime when stream was added/removed from queue instead of waiting for a onPlay/onPause action to happen - when popup or background players start the stream will start playing only if paused state wasn't requested. Which means, for example, if a user opens popup it will be started when START_PAUSED is false. If, for example, the stream was played in main player and then popup was started the stream will still be playing, but if it was paused it still be paused in popup (or background) in APPEND_ONLY mode (but will be playing on new queue initialization)
This commit is contained in:
parent
160a04c3c7
commit
c1d5a5cd98
|
@ -20,7 +20,10 @@
|
||||||
|
|
||||||
package org.schabi.newpipe;
|
package org.schabi.newpipe;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -101,6 +104,8 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private boolean servicesShown = false;
|
private boolean servicesShown = false;
|
||||||
private ImageView serviceArrow;
|
private ImageView serviceArrow;
|
||||||
|
|
||||||
|
private BroadcastReceiver broadcastReceiver;
|
||||||
|
|
||||||
private static final int ITEM_ID_SUBSCRIPTIONS = -1;
|
private static final int ITEM_ID_SUBSCRIPTIONS = -1;
|
||||||
private static final int ITEM_ID_FEED = -2;
|
private static final int ITEM_ID_FEED = -2;
|
||||||
private static final int ITEM_ID_BOOKMARKS = -3;
|
private static final int ITEM_ID_BOOKMARKS = -3;
|
||||||
|
@ -147,6 +152,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
if (DeviceUtils.isTv(this)) {
|
if (DeviceUtils.isTv(this)) {
|
||||||
FocusOverlayView.setupFocusObserver(this);
|
FocusOverlayView.setupFocusObserver(this);
|
||||||
}
|
}
|
||||||
|
setupBroadcastReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupDrawer() throws Exception {
|
private void setupDrawer() throws Exception {
|
||||||
|
@ -454,6 +460,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
if (!isChangingConfigurations()) {
|
if (!isChangingConfigurations()) {
|
||||||
StateSaver.clearStateFiles();
|
StateSaver.clearStateFiles();
|
||||||
}
|
}
|
||||||
|
unregisterReceiver(broadcastReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -795,9 +802,24 @@ public class MainActivity extends AppCompatActivity {
|
||||||
ErrorActivity.reportUiError(this, e);
|
ErrorActivity.reportUiError(this, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Utils
|
private void setupBroadcastReceiver() {
|
||||||
* */
|
broadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
|
if (intent.getAction().equals(VideoDetailFragment.ACTION_PLAYER_STARTED)) {
|
||||||
|
final Fragment fragmentPlayer = getSupportFragmentManager()
|
||||||
|
.findFragmentById(R.id.fragment_player_holder);
|
||||||
|
if (fragmentPlayer == null) {
|
||||||
|
NavigationHelper.showMiniPlayer(getSupportFragmentManager());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
final IntentFilter intentFilter = new IntentFilter();
|
||||||
|
intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED);
|
||||||
|
registerReceiver(broadcastReceiver, intentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean bottomSheetHiddenOrCollapsed() {
|
private boolean bottomSheetHiddenOrCollapsed() {
|
||||||
final FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder);
|
final FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder);
|
||||||
|
|
|
@ -146,6 +146,8 @@ public class VideoDetailFragment
|
||||||
"org.schabi.newpipe.VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
|
"org.schabi.newpipe.VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER";
|
||||||
public static final String ACTION_HIDE_MAIN_PLAYER =
|
public static final String ACTION_HIDE_MAIN_PLAYER =
|
||||||
"org.schabi.newpipe.VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
|
"org.schabi.newpipe.VideoDetailFragment.ACTION_HIDE_MAIN_PLAYER";
|
||||||
|
public static final String ACTION_PLAYER_STARTED =
|
||||||
|
"org.schabi.newpipe.VideoDetailFragment.ACTION_PLAYER_STARTED";
|
||||||
public static final String ACTION_VIDEO_FRAGMENT_RESUMED =
|
public static final String ACTION_VIDEO_FRAGMENT_RESUMED =
|
||||||
"org.schabi.newpipe.VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED";
|
"org.schabi.newpipe.VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED";
|
||||||
public static final String ACTION_VIDEO_FRAGMENT_STOPPED =
|
public static final String ACTION_VIDEO_FRAGMENT_STOPPED =
|
||||||
|
@ -302,6 +304,12 @@ public class VideoDetailFragment
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VideoDetailFragment getInstanceCollapsed() {
|
||||||
|
final VideoDetailFragment instance = new VideoDetailFragment();
|
||||||
|
instance.bottomSheetState = BottomSheetBehavior.STATE_COLLAPSED;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Fragment's Lifecycle
|
// Fragment's Lifecycle
|
||||||
|
@ -518,7 +526,7 @@ public class VideoDetailFragment
|
||||||
openVideoPlayer();
|
openVideoPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
setOverlayPlayPauseImage();
|
setOverlayPlayPauseImage(player != null && player.isPlaying());
|
||||||
break;
|
break;
|
||||||
case R.id.overlay_close_button:
|
case R.id.overlay_close_button:
|
||||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||||
|
@ -1325,12 +1333,22 @@ public class VideoDetailFragment
|
||||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||||
} else if (intent.getAction().equals(ACTION_HIDE_MAIN_PLAYER)) {
|
} else if (intent.getAction().equals(ACTION_HIDE_MAIN_PLAYER)) {
|
||||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||||
|
} else if (intent.getAction().equals(ACTION_PLAYER_STARTED)) {
|
||||||
|
// If the state is not hidden we don't need to show the mini player
|
||||||
|
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
|
||||||
|
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||||
|
}
|
||||||
|
// Rebound to the service if it was closed via notification or mini player
|
||||||
|
if (!PlayerHolder.bound) {
|
||||||
|
PlayerHolder.startService(App.getApp(), false, VideoDetailFragment.this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
final IntentFilter intentFilter = new IntentFilter();
|
final IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(ACTION_SHOW_MAIN_PLAYER);
|
intentFilter.addAction(ACTION_SHOW_MAIN_PLAYER);
|
||||||
intentFilter.addAction(ACTION_HIDE_MAIN_PLAYER);
|
intentFilter.addAction(ACTION_HIDE_MAIN_PLAYER);
|
||||||
|
intentFilter.addAction(ACTION_PLAYER_STARTED);
|
||||||
activity.registerReceiver(broadcastReceiver, intentFilter);
|
activity.registerReceiver(broadcastReceiver, intentFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1719,8 +1737,12 @@ public class VideoDetailFragment
|
||||||
// It will allow to have live instance of PlayQueue with actual information about
|
// It will allow to have live instance of PlayQueue with actual information about
|
||||||
// deleted/added items inside Channel/Playlist queue and makes possible to have
|
// deleted/added items inside Channel/Playlist queue and makes possible to have
|
||||||
// a history of played items
|
// a history of played items
|
||||||
if (stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue)) {
|
if ((stack.isEmpty() || !stack.peek().getPlayQueue().equals(queue)
|
||||||
stack.push(new StackItem(serviceId, url, name, playQueue));
|
&& queue.getItem() != null)) {
|
||||||
|
stack.push(new StackItem(queue.getItem().getServiceId(),
|
||||||
|
queue.getItem().getUrl(),
|
||||||
|
queue.getItem().getTitle(),
|
||||||
|
queue));
|
||||||
} else {
|
} else {
|
||||||
final StackItem stackWithQueue = findQueueInStack(queue);
|
final StackItem stackWithQueue = findQueueInStack(queue);
|
||||||
if (stackWithQueue != null) {
|
if (stackWithQueue != null) {
|
||||||
|
@ -1744,7 +1766,7 @@ public class VideoDetailFragment
|
||||||
final int repeatMode,
|
final int repeatMode,
|
||||||
final boolean shuffled,
|
final boolean shuffled,
|
||||||
final PlaybackParameters parameters) {
|
final PlaybackParameters parameters) {
|
||||||
setOverlayPlayPauseImage();
|
setOverlayPlayPauseImage(player != null && player.isPlaying());
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case BasePlayer.STATE_PLAYING:
|
case BasePlayer.STATE_PLAYING:
|
||||||
|
@ -1818,7 +1840,7 @@ public class VideoDetailFragment
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onServiceStopped() {
|
public void onServiceStopped() {
|
||||||
setOverlayPlayPauseImage();
|
setOverlayPlayPauseImage(false);
|
||||||
if (currentInfo != null) {
|
if (currentInfo != null) {
|
||||||
updateOverlayData(currentInfo.getName(),
|
updateOverlayData(currentInfo.getName(),
|
||||||
currentInfo.getUploaderName(),
|
currentInfo.getUploaderName(),
|
||||||
|
@ -2224,6 +2246,9 @@ public class VideoDetailFragment
|
||||||
break;
|
break;
|
||||||
case BottomSheetBehavior.STATE_COLLAPSED:
|
case BottomSheetBehavior.STATE_COLLAPSED:
|
||||||
moveFocusToMainFragment(true);
|
moveFocusToMainFragment(true);
|
||||||
|
manageSpaceAtTheBottom(false);
|
||||||
|
|
||||||
|
bottomSheetBehavior.setPeekHeight(peekHeight);
|
||||||
|
|
||||||
// Re-enable clicks
|
// Re-enable clicks
|
||||||
setOverlayElementsClickable(true);
|
setOverlayElementsClickable(true);
|
||||||
|
@ -2270,8 +2295,8 @@ public class VideoDetailFragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setOverlayPlayPauseImage() {
|
private void setOverlayPlayPauseImage(final boolean playerIsPlaying) {
|
||||||
final int attr = player != null && player.isPlaying()
|
final int attr = playerIsPlaying
|
||||||
? R.attr.ic_pause
|
? R.attr.ic_pause
|
||||||
: R.attr.ic_play_arrow;
|
: R.attr.ic_play_arrow;
|
||||||
overlayPlayPauseButton.setImageResource(
|
overlayPlayPauseButton.setImageResource(
|
||||||
|
|
|
@ -255,10 +255,10 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
onQueueClosed();
|
onQueueClosed();
|
||||||
// Android TV: without it focus will frame the whole player
|
// Android TV: without it focus will frame the whole player
|
||||||
playPauseButton.requestFocus();
|
playPauseButton.requestFocus();
|
||||||
}
|
|
||||||
|
|
||||||
onPlay();
|
onPlay();
|
||||||
}
|
}
|
||||||
|
NavigationHelper.sendPlayerStartedEvent(service);
|
||||||
|
}
|
||||||
|
|
||||||
VideoPlayerImpl(final MainPlayer service) {
|
VideoPlayerImpl(final MainPlayer service) {
|
||||||
super("MainPlayer" + TAG, service);
|
super("MainPlayer" + TAG, service);
|
||||||
|
@ -662,6 +662,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
@Override
|
@Override
|
||||||
public void onPlayQueueEdited() {
|
public void onPlayQueueEdited() {
|
||||||
updatePlayback();
|
updatePlayback();
|
||||||
|
showOrHideButtons();
|
||||||
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1455,15 +1456,16 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
playPreviousButton.setVisibility(playQueue.getIndex() == 0
|
final boolean showPrev = playQueue.getIndex() != 0;
|
||||||
? View.INVISIBLE
|
final boolean showNext = playQueue.getIndex() + 1 != playQueue.getStreams().size();
|
||||||
: View.VISIBLE);
|
final boolean showQueue = playQueue.getStreams().size() > 1 && !popupPlayerSelected();
|
||||||
playNextButton.setVisibility(playQueue.getIndex() + 1 == playQueue.getStreams().size()
|
|
||||||
? View.INVISIBLE
|
playPreviousButton.setVisibility(showPrev ? View.VISIBLE : View.INVISIBLE);
|
||||||
: View.VISIBLE);
|
playPreviousButton.setAlpha(showPrev ? 1.0f : 0.0f);
|
||||||
queueButton.setVisibility(playQueue.getStreams().size() <= 1 || popupPlayerSelected()
|
playNextButton.setVisibility(showNext ? View.VISIBLE : View.INVISIBLE);
|
||||||
? View.GONE
|
playNextButton.setAlpha(showNext ? 1.0f : 0.0f);
|
||||||
: View.VISIBLE);
|
queueButton.setVisibility(showQueue ? View.VISIBLE : View.GONE);
|
||||||
|
queueButton.setAlpha(showQueue ? 1.0f : 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSystemUIPartially() {
|
private void showSystemUIPartially() {
|
||||||
|
@ -1936,6 +1938,7 @@ public class VideoPlayerImpl extends VideoPlayer
|
||||||
getControlsRoot().setPadding(0, 0, 0, 0);
|
getControlsRoot().setPadding(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
queueLayout.setPadding(0, 0, 0, 0);
|
queueLayout.setPadding(0, 0, 0, 0);
|
||||||
|
updateQueue();
|
||||||
updateMetadata();
|
updateMetadata();
|
||||||
updatePlayback();
|
updatePlayback();
|
||||||
triggerProgressUpdate();
|
triggerProgressUpdate();
|
||||||
|
|
|
@ -384,8 +384,19 @@ public final class NavigationHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void expandMainPlayer(final Context context) {
|
public static void expandMainPlayer(final Context context) {
|
||||||
final Intent intent = new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER);
|
context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER));
|
||||||
context.sendBroadcast(intent);
|
}
|
||||||
|
|
||||||
|
public static void sendPlayerStartedEvent(final Context context) {
|
||||||
|
context.sendBroadcast(new Intent(VideoDetailFragment.ACTION_PLAYER_STARTED));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showMiniPlayer(final FragmentManager fragmentManager) {
|
||||||
|
final VideoDetailFragment instance = VideoDetailFragment.getInstanceCollapsed();
|
||||||
|
defaultTransaction(fragmentManager)
|
||||||
|
.replace(R.id.fragment_player_holder, instance)
|
||||||
|
.runOnCommit(() -> sendPlayerStartedEvent(instance.requireActivity()))
|
||||||
|
.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openChannelFragment(final FragmentManager fragmentManager,
|
public static void openChannelFragment(final FragmentManager fragmentManager,
|
||||||
|
|
Loading…
Reference in New Issue