mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 16:40:32 +00:00
Create MediaSessionPlayerUi
This commit is contained in:
parent
ce6f3ca5df
commit
c054ea0737
@ -99,14 +99,13 @@ import org.schabi.newpipe.player.event.PlayerEventListener;
|
|||||||
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
|
import org.schabi.newpipe.player.event.PlayerServiceEventListener;
|
||||||
import org.schabi.newpipe.player.helper.AudioReactor;
|
import org.schabi.newpipe.player.helper.AudioReactor;
|
||||||
import org.schabi.newpipe.player.helper.LoadController;
|
import org.schabi.newpipe.player.helper.LoadController;
|
||||||
import org.schabi.newpipe.player.helper.MediaSessionManager;
|
|
||||||
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
import org.schabi.newpipe.player.mediaitem.MediaItemTag;
|
import org.schabi.newpipe.player.mediaitem.MediaItemTag;
|
||||||
|
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
|
||||||
import org.schabi.newpipe.player.notification.NotificationPlayerUi;
|
import org.schabi.newpipe.player.notification.NotificationPlayerUi;
|
||||||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||||
import org.schabi.newpipe.player.playback.PlayerMediaSession;
|
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
import org.schabi.newpipe.player.playqueue.PlayQueue;
|
||||||
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
import org.schabi.newpipe.player.playqueue.PlayQueueItem;
|
||||||
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
|
import org.schabi.newpipe.player.resolver.AudioPlaybackResolver;
|
||||||
@ -196,7 +195,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
|
|
||||||
private ExoPlayer simpleExoPlayer;
|
private ExoPlayer simpleExoPlayer;
|
||||||
private AudioReactor audioReactor;
|
private AudioReactor audioReactor;
|
||||||
private MediaSessionManager mediaSessionManager;
|
|
||||||
|
|
||||||
@NonNull private final DefaultTrackSelector trackSelector;
|
@NonNull private final DefaultTrackSelector trackSelector;
|
||||||
@NonNull private final LoadController loadController;
|
@NonNull private final LoadController loadController;
|
||||||
@ -225,7 +223,7 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
@SuppressWarnings("MemberName") // keep the unusual member name
|
@SuppressWarnings("MemberName") // keep the unusual member name
|
||||||
private final PlayerUiList UIs = new PlayerUiList();
|
private final PlayerUiList UIs;
|
||||||
|
|
||||||
private BroadcastReceiver broadcastReceiver;
|
private BroadcastReceiver broadcastReceiver;
|
||||||
private IntentFilter intentFilter;
|
private IntentFilter intentFilter;
|
||||||
@ -265,6 +263,15 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
|
|
||||||
videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
|
videoResolver = new VideoPlaybackResolver(context, dataSource, getQualityResolver());
|
||||||
audioResolver = new AudioPlaybackResolver(context, dataSource);
|
audioResolver = new AudioPlaybackResolver(context, dataSource);
|
||||||
|
|
||||||
|
// The UIs added here should always be present. They will be initialized when the player
|
||||||
|
// reaches the initialization step. Make sure the media session ui is before the
|
||||||
|
// notification ui in the UIs list, since the notification depends on the media session in
|
||||||
|
// PlayerUi#initPlayer(), and UIs.call() guarantees UI order is preserved.
|
||||||
|
UIs = new PlayerUiList(
|
||||||
|
new MediaSessionPlayerUi(this),
|
||||||
|
new NotificationPlayerUi(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private VideoPlaybackResolver.QualityResolver getQualityResolver() {
|
private VideoPlaybackResolver.QualityResolver getQualityResolver() {
|
||||||
@ -431,11 +438,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initUIsForCurrentPlayerType() {
|
private void initUIsForCurrentPlayerType() {
|
||||||
//noinspection SimplifyOptionalCallChains
|
|
||||||
if (!UIs.get(NotificationPlayerUi.class).isPresent()) {
|
|
||||||
UIs.addAndPrepare(new NotificationPlayerUi(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((UIs.get(MainPlayerUi.class).isPresent() && playerType == PlayerType.MAIN)
|
if ((UIs.get(MainPlayerUi.class).isPresent() && playerType == PlayerType.MAIN)
|
||||||
|| (UIs.get(PopupPlayerUi.class).isPresent() && playerType == PlayerType.POPUP)) {
|
|| (UIs.get(PopupPlayerUi.class).isPresent() && playerType == PlayerType.POPUP)) {
|
||||||
// correct UI already in place
|
// correct UI already in place
|
||||||
@ -506,8 +508,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
simpleExoPlayer.setHandleAudioBecomingNoisy(true);
|
simpleExoPlayer.setHandleAudioBecomingNoisy(true);
|
||||||
|
|
||||||
audioReactor = new AudioReactor(context, simpleExoPlayer);
|
audioReactor = new AudioReactor(context, simpleExoPlayer);
|
||||||
mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer,
|
|
||||||
new PlayerMediaSession(this));
|
|
||||||
|
|
||||||
registerBroadcastReceiver();
|
registerBroadcastReceiver();
|
||||||
|
|
||||||
@ -558,9 +558,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
if (playQueueManager != null) {
|
if (playQueueManager != null) {
|
||||||
playQueueManager.dispose();
|
playQueueManager.dispose();
|
||||||
}
|
}
|
||||||
if (mediaSessionManager != null) {
|
|
||||||
mediaSessionManager.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
@ -723,11 +720,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
Log.d(TAG, "ACTION_CONFIGURATION_CHANGED received");
|
Log.d(TAG, "ACTION_CONFIGURATION_CHANGED received");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Intent.ACTION_HEADSET_PLUG: //FIXME
|
|
||||||
/*notificationManager.cancel(NOTIFICATION_ID);
|
|
||||||
mediaSessionManager.dispose();
|
|
||||||
mediaSessionManager.enable(getBaseContext(), basePlayerImpl.simpleExoPlayer);*/
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UIs.call(playerUi -> playerUi.onBroadcastReceived(intent));
|
UIs.call(playerUi -> playerUi.onBroadcastReceived(intent));
|
||||||
@ -1738,15 +1730,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
initThumbnail(info.getThumbnailUrl());
|
initThumbnail(info.getThumbnailUrl());
|
||||||
registerStreamViewed();
|
registerStreamViewed();
|
||||||
|
|
||||||
final boolean showThumbnail = prefs.getBoolean(
|
|
||||||
context.getString(R.string.show_thumbnail_key), true);
|
|
||||||
mediaSessionManager.setMetadata(
|
|
||||||
getVideoTitle(),
|
|
||||||
getUploaderName(),
|
|
||||||
showThumbnail ? Optional.ofNullable(getThumbnail()) : Optional.empty(),
|
|
||||||
StreamTypeUtil.isLiveStream(info.getStreamType()) ? -1 : info.getDuration()
|
|
||||||
);
|
|
||||||
|
|
||||||
notifyMetadataUpdateToListeners();
|
notifyMetadataUpdateToListeners();
|
||||||
UIs.call(playerUi -> playerUi.onMetadataChanged(info));
|
UIs.call(playerUi -> playerUi.onMetadataChanged(info));
|
||||||
}
|
}
|
||||||
@ -2194,10 +2177,6 @@ public final class Player implements PlaybackListener, Listener {
|
|||||||
return prefs;
|
return prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MediaSessionManager getMediaSessionManager() {
|
|
||||||
return mediaSessionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public PlayerType getPlayerType() {
|
public PlayerType getPlayerType() {
|
||||||
return playerType;
|
return playerType;
|
||||||
|
@ -28,6 +28,7 @@ import android.os.Binder;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
|
||||||
import org.schabi.newpipe.util.ThemeHelper;
|
import org.schabi.newpipe.util.ThemeHelper;
|
||||||
|
|
||||||
|
|
||||||
@ -73,9 +74,8 @@ public final class PlayerService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
player.handleIntent(intent);
|
player.handleIntent(intent);
|
||||||
if (player.getMediaSessionManager() != null) {
|
player.UIs().get(MediaSessionPlayerUi.class)
|
||||||
player.getMediaSessionManager().handleMediaButtonIntent(intent);
|
.ifPresent(ui -> ui.handleMediaButtonIntent(intent));
|
||||||
}
|
|
||||||
|
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.schabi.newpipe.player.helper;
|
package org.schabi.newpipe.player.mediasession;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -18,8 +18,6 @@ import com.google.android.exoplayer2.Player;
|
|||||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||||
|
|
||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
|
||||||
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
@ -0,0 +1,74 @@
|
|||||||
|
package org.schabi.newpipe.player.mediasession;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.R;
|
||||||
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
|
import org.schabi.newpipe.player.Player;
|
||||||
|
import org.schabi.newpipe.player.playback.PlayerMediaSession;
|
||||||
|
import org.schabi.newpipe.player.ui.PlayerUi;
|
||||||
|
import org.schabi.newpipe.util.StreamTypeUtil;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class MediaSessionPlayerUi extends PlayerUi {
|
||||||
|
|
||||||
|
private MediaSessionManager mediaSessionManager;
|
||||||
|
|
||||||
|
public MediaSessionPlayerUi(@NonNull final Player player) {
|
||||||
|
super(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initPlayer() {
|
||||||
|
super.initPlayer();
|
||||||
|
if (mediaSessionManager != null) {
|
||||||
|
mediaSessionManager.dispose();
|
||||||
|
}
|
||||||
|
mediaSessionManager = new MediaSessionManager(context, player.getExoPlayer(),
|
||||||
|
new PlayerMediaSession(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyPlayer() {
|
||||||
|
super.destroyPlayer();
|
||||||
|
if (mediaSessionManager != null) {
|
||||||
|
mediaSessionManager.dispose();
|
||||||
|
mediaSessionManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBroadcastReceived(final Intent intent) {
|
||||||
|
super.onBroadcastReceived(intent);
|
||||||
|
// TODO decide whether to handle ACTION_HEADSET_PLUG or not
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMetadataChanged(@NonNull final StreamInfo info) {
|
||||||
|
super.onMetadataChanged(info);
|
||||||
|
|
||||||
|
final boolean showThumbnail = player.getPrefs().getBoolean(
|
||||||
|
context.getString(R.string.show_thumbnail_key), true);
|
||||||
|
|
||||||
|
mediaSessionManager.setMetadata(
|
||||||
|
player.getVideoTitle(),
|
||||||
|
player.getUploaderName(),
|
||||||
|
showThumbnail ? Optional.ofNullable(player.getThumbnail()) : Optional.empty(),
|
||||||
|
StreamTypeUtil.isLiveStream(info.getStreamType()) ? -1 : info.getDuration()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleMediaButtonIntent(final Intent intent) {
|
||||||
|
if (mediaSessionManager != null) {
|
||||||
|
mediaSessionManager.handleMediaButtonIntent(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<MediaSessionCompat.Token> getSessionToken() {
|
||||||
|
return Optional.ofNullable(mediaSessionManager).map(MediaSessionManager::getSessionToken);
|
||||||
|
}
|
||||||
|
}
|
@ -19,11 +19,13 @@ import androidx.core.content.ContextCompat;
|
|||||||
import org.schabi.newpipe.MainActivity;
|
import org.schabi.newpipe.MainActivity;
|
||||||
import org.schabi.newpipe.R;
|
import org.schabi.newpipe.R;
|
||||||
import org.schabi.newpipe.player.Player;
|
import org.schabi.newpipe.player.Player;
|
||||||
|
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
|
||||||
import org.schabi.newpipe.util.NavigationHelper;
|
import org.schabi.newpipe.util.NavigationHelper;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
|
import static androidx.media.app.NotificationCompat.MediaStyle;
|
||||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL;
|
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL;
|
||||||
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE;
|
import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE;
|
||||||
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE;
|
import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE;
|
||||||
@ -101,9 +103,11 @@ public final class NotificationUtil {
|
|||||||
player.getContext(), player.getPrefs(), nonNothingSlotCount);
|
player.getContext(), player.getPrefs(), nonNothingSlotCount);
|
||||||
final int[] compactSlots = compactSlotList.stream().mapToInt(Integer::intValue).toArray();
|
final int[] compactSlots = compactSlotList.stream().mapToInt(Integer::intValue).toArray();
|
||||||
|
|
||||||
builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle()
|
final MediaStyle mediaStyle = new MediaStyle().setShowActionsInCompactView(compactSlots);
|
||||||
.setMediaSession(player.getMediaSessionManager().getSessionToken())
|
player.UIs().get(MediaSessionPlayerUi.class).flatMap(MediaSessionPlayerUi::getSessionToken)
|
||||||
.setShowActionsInCompactView(compactSlots))
|
.ifPresent(mediaStyle::setMediaSession);
|
||||||
|
|
||||||
|
builder.setStyle(mediaStyle)
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
||||||
|
@ -29,7 +29,8 @@ public abstract class PlayerUi {
|
|||||||
@NonNull protected final Player player;
|
@NonNull protected final Player player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param player the player instance that will be usable throughout the lifetime of this UI
|
* @param player the player instance that will be usable throughout the lifetime of this UI; its
|
||||||
|
* context should already have been initialized
|
||||||
*/
|
*/
|
||||||
protected PlayerUi(@NonNull final Player player) {
|
protected PlayerUi(@NonNull final Player player) {
|
||||||
this.context = player.getContext();
|
this.context = player.getContext();
|
||||||
|
@ -8,6 +8,19 @@ import java.util.function.Consumer;
|
|||||||
public final class PlayerUiList {
|
public final class PlayerUiList {
|
||||||
final List<PlayerUi> playerUis = new ArrayList<>();
|
final List<PlayerUi> playerUis = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link PlayerUiList} starting with the provided player uis. The provided player uis
|
||||||
|
* will not be prepared like those passed to {@link #addAndPrepare(PlayerUi)}, because when
|
||||||
|
* the {@link PlayerUiList} constructor is called, the player is still not running and it
|
||||||
|
* wouldn't make sense to initialize uis then. Instead the player will initialize them by doing
|
||||||
|
* proper calls to {@link #call(Consumer)}.
|
||||||
|
*
|
||||||
|
* @param initialPlayerUis the player uis this list should start with; the order will be kept
|
||||||
|
*/
|
||||||
|
public PlayerUiList(final PlayerUi... initialPlayerUis) {
|
||||||
|
playerUis.addAll(List.of(initialPlayerUis));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the provided player ui to the list and calls on it the initialization functions that
|
* Adds the provided player ui to the list and calls on it the initialization functions that
|
||||||
* apply based on the current player state. The preparation step needs to be done since when UIs
|
* apply based on the current player state. The preparation step needs to be done since when UIs
|
||||||
@ -67,11 +80,11 @@ public final class PlayerUiList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls the provided consumer on all player UIs in the list.
|
* Calls the provided consumer on all player UIs in the list, in order of addition.
|
||||||
* @param consumer the consumer to call with player UIs
|
* @param consumer the consumer to call with player UIs
|
||||||
*/
|
*/
|
||||||
public void call(final Consumer<PlayerUi> consumer) {
|
public void call(final Consumer<PlayerUi> consumer) {
|
||||||
//noinspection SimplifyStreamApiCallChains
|
//noinspection SimplifyStreamApiCallChains
|
||||||
playerUis.stream().forEach(consumer);
|
playerUis.stream().forEachOrdered(consumer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user