mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-04-14 14:53:16 +00:00
Instantiate media session and connector in PlayerService
This changes significantly how the MediaSessionCompat and MediaSessionConnector objects are used: - now they are tied to the service and not to the player, and so they might be reused with multiple players (which should be allowed) - now they can exist even if there is no player (which is fundamental to be able to answer media browser queries)
This commit is contained in:
parent
5819546ea9
commit
7d17468266
@ -55,6 +55,7 @@ import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioManager;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
@ -71,6 +72,7 @@ import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player.PositionInfo;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Tracks;
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.text.CueGroup;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
@ -269,7 +271,16 @@ public final class Player implements PlaybackListener, Listener {
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
//region Constructor
|
||||
|
||||
public Player(@NonNull final PlayerService service) {
|
||||
/**
|
||||
* @param service the service this player resides in
|
||||
* @param mediaSession used to build the {@link MediaSessionPlayerUi}, lives in the service and
|
||||
* could possibly be reused with multiple player instances
|
||||
* @param sessionConnector used to build the {@link MediaSessionPlayerUi}, lives in the service
|
||||
* and could possibly be reused with multiple player instances
|
||||
*/
|
||||
public Player(@NonNull final PlayerService service,
|
||||
@NonNull final MediaSessionCompat mediaSession,
|
||||
@NonNull final MediaSessionConnector sessionConnector) {
|
||||
this.service = service;
|
||||
context = service;
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
@ -302,7 +313,7 @@ public final class Player implements PlaybackListener, Listener {
|
||||
// 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 MediaSessionPlayerUi(this, mediaSession, sessionConnector),
|
||||
new NotificationPlayerUi(this)
|
||||
);
|
||||
}
|
||||
|
@ -27,12 +27,15 @@ import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.media.MediaBrowserCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media.MediaBrowserServiceCompat;
|
||||
|
||||
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||
|
||||
import org.schabi.newpipe.ktx.BundleKt;
|
||||
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
|
||||
import org.schabi.newpipe.player.notification.NotificationPlayerUi;
|
||||
@ -52,6 +55,12 @@ public final class PlayerService extends MediaBrowserServiceCompat {
|
||||
public static final String SHOULD_START_FOREGROUND_EXTRA = "should_start_foreground_extra";
|
||||
public static final String BIND_PLAYER_HOLDER_ACTION = "bind_player_holder_action";
|
||||
|
||||
// these are instantiated in onCreate() as per
|
||||
// https://developer.android.com/training/cars/media#browser_workflow
|
||||
private MediaSessionCompat mediaSession;
|
||||
private MediaSessionConnector sessionConnector;
|
||||
|
||||
@Nullable
|
||||
private Player player;
|
||||
|
||||
private final IBinder mBinder = new PlayerService.LocalBinder(this);
|
||||
@ -71,6 +80,12 @@ public final class PlayerService extends MediaBrowserServiceCompat {
|
||||
assureCorrectAppLanguage(this);
|
||||
ThemeHelper.setTheme(this);
|
||||
|
||||
// see https://developer.android.com/training/cars/media#browser_workflow
|
||||
mediaSession = new MediaSessionCompat(this, "MediaSessionPlayerServ");
|
||||
setSessionToken(mediaSession.getSessionToken());
|
||||
sessionConnector = new MediaSessionConnector(mediaSession);
|
||||
sessionConnector.setMetadataDeduplicationEnabled(true);
|
||||
|
||||
// Note: you might be tempted to create the player instance and call startForeground here,
|
||||
// but be aware that the Android system might start the service just to perform media
|
||||
// queries. In those cases creating a player instance is a waste of resources, and calling
|
||||
@ -94,7 +109,7 @@ public final class PlayerService extends MediaBrowserServiceCompat {
|
||||
if (intent.getBooleanExtra(SHOULD_START_FOREGROUND_EXTRA, false)) {
|
||||
if (player == null) {
|
||||
// make sure the player exists, in case the service was resumed
|
||||
player = new Player(this);
|
||||
player = new Player(this, mediaSession, sessionConnector);
|
||||
}
|
||||
|
||||
// Be sure that the player notification is set and the service is started in foreground,
|
||||
@ -159,7 +174,11 @@ public final class PlayerService extends MediaBrowserServiceCompat {
|
||||
Log.d(TAG, "destroy() called");
|
||||
}
|
||||
super.onDestroy();
|
||||
|
||||
cleanup();
|
||||
|
||||
mediaSession.setActive(false);
|
||||
mediaSession.release();
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
|
@ -38,10 +38,10 @@ public class MediaSessionPlayerUi extends PlayerUi
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private static final String TAG = "MediaSessUi";
|
||||
|
||||
@Nullable
|
||||
private MediaSessionCompat mediaSession;
|
||||
@Nullable
|
||||
private MediaSessionConnector sessionConnector;
|
||||
@NonNull
|
||||
private final MediaSessionCompat mediaSession;
|
||||
@NonNull
|
||||
private final MediaSessionConnector sessionConnector;
|
||||
|
||||
private final String ignoreHardwareMediaButtonsKey;
|
||||
private boolean shouldIgnoreHardwareMediaButtons = false;
|
||||
@ -50,9 +50,13 @@ public class MediaSessionPlayerUi extends PlayerUi
|
||||
private List<NotificationActionData> prevNotificationActions = List.of();
|
||||
|
||||
|
||||
public MediaSessionPlayerUi(@NonNull final Player player) {
|
||||
public MediaSessionPlayerUi(@NonNull final Player player,
|
||||
@NonNull final MediaSessionCompat mediaSession,
|
||||
@NonNull final MediaSessionConnector sessionConnector) {
|
||||
super(player);
|
||||
ignoreHardwareMediaButtonsKey =
|
||||
this.mediaSession = mediaSession;
|
||||
this.sessionConnector = sessionConnector;
|
||||
this.ignoreHardwareMediaButtonsKey =
|
||||
context.getString(R.string.ignore_hardware_media_buttons_key);
|
||||
}
|
||||
|
||||
@ -61,10 +65,8 @@ public class MediaSessionPlayerUi extends PlayerUi
|
||||
super.initPlayer();
|
||||
destroyPlayer(); // release previously used resources
|
||||
|
||||
mediaSession = new MediaSessionCompat(context, TAG);
|
||||
mediaSession.setActive(true);
|
||||
|
||||
sessionConnector = new MediaSessionConnector(mediaSession);
|
||||
sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, player));
|
||||
sessionConnector.setPlayer(getForwardingPlayer());
|
||||
|
||||
@ -89,27 +91,18 @@ public class MediaSessionPlayerUi extends PlayerUi
|
||||
public void destroyPlayer() {
|
||||
super.destroyPlayer();
|
||||
player.getPrefs().unregisterOnSharedPreferenceChangeListener(this);
|
||||
if (sessionConnector != null) {
|
||||
sessionConnector.setMediaButtonEventHandler(null);
|
||||
sessionConnector.setPlayer(null);
|
||||
sessionConnector.setQueueNavigator(null);
|
||||
sessionConnector = null;
|
||||
}
|
||||
if (mediaSession != null) {
|
||||
mediaSession.setActive(false);
|
||||
mediaSession.release();
|
||||
mediaSession = null;
|
||||
}
|
||||
sessionConnector.setMediaButtonEventHandler(null);
|
||||
sessionConnector.setPlayer(null);
|
||||
sessionConnector.setQueueNavigator(null);
|
||||
mediaSession.setActive(false);
|
||||
prevNotificationActions = List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
|
||||
super.onThumbnailLoaded(bitmap);
|
||||
if (sessionConnector != null) {
|
||||
// the thumbnail is now loaded: invalidate the metadata to trigger a metadata update
|
||||
sessionConnector.invalidateMediaSessionMetadata();
|
||||
}
|
||||
// the thumbnail is now loaded: invalidate the metadata to trigger a metadata update
|
||||
sessionConnector.invalidateMediaSessionMetadata();
|
||||
}
|
||||
|
||||
|
||||
@ -200,8 +193,8 @@ public class MediaSessionPlayerUi extends PlayerUi
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionConnector == null) {
|
||||
// sessionConnector will be null after destroyPlayer is called
|
||||
if (!mediaSession.isActive()) {
|
||||
// mediaSession will be inactive after destroyPlayer is called
|
||||
return;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user