Skip to content

Commit 7d17468

Browse files
committed
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)
1 parent 5819546 commit 7d17468

3 files changed

Lines changed: 51 additions & 28 deletions

File tree

app/src/main/java/org/schabi/newpipe/player/Player.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import android.graphics.Bitmap;
5656
import android.graphics.drawable.Drawable;
5757
import android.media.AudioManager;
58+
import android.support.v4.media.session.MediaSessionCompat;
5859
import android.util.Log;
5960
import android.view.LayoutInflater;
6061

@@ -71,6 +72,7 @@
7172
import com.google.android.exoplayer2.Player.PositionInfo;
7273
import com.google.android.exoplayer2.Timeline;
7374
import com.google.android.exoplayer2.Tracks;
75+
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
7476
import com.google.android.exoplayer2.source.MediaSource;
7577
import com.google.android.exoplayer2.text.CueGroup;
7678
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
@@ -269,7 +271,16 @@ public final class Player implements PlaybackListener, Listener {
269271
//////////////////////////////////////////////////////////////////////////*/
270272
//region Constructor
271273

272-
public Player(@NonNull final PlayerService service) {
274+
/**
275+
* @param service the service this player resides in
276+
* @param mediaSession used to build the {@link MediaSessionPlayerUi}, lives in the service and
277+
* could possibly be reused with multiple player instances
278+
* @param sessionConnector used to build the {@link MediaSessionPlayerUi}, lives in the service
279+
* and could possibly be reused with multiple player instances
280+
*/
281+
public Player(@NonNull final PlayerService service,
282+
@NonNull final MediaSessionCompat mediaSession,
283+
@NonNull final MediaSessionConnector sessionConnector) {
273284
this.service = service;
274285
context = service;
275286
prefs = PreferenceManager.getDefaultSharedPreferences(context);
@@ -302,7 +313,7 @@ public Player(@NonNull final PlayerService service) {
302313
// notification ui in the UIs list, since the notification depends on the media session in
303314
// PlayerUi#initPlayer(), and UIs.call() guarantees UI order is preserved.
304315
UIs = new PlayerUiList(
305-
new MediaSessionPlayerUi(this),
316+
new MediaSessionPlayerUi(this, mediaSession, sessionConnector),
306317
new NotificationPlayerUi(this)
307318
);
308319
}

app/src/main/java/org/schabi/newpipe/player/PlayerService.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,15 @@
2727
import android.os.Bundle;
2828
import android.os.IBinder;
2929
import android.support.v4.media.MediaBrowserCompat;
30+
import android.support.v4.media.session.MediaSessionCompat;
3031
import android.util.Log;
3132

3233
import androidx.annotation.NonNull;
3334
import androidx.annotation.Nullable;
3435
import androidx.media.MediaBrowserServiceCompat;
3536

37+
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
38+
3639
import org.schabi.newpipe.ktx.BundleKt;
3740
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
3841
import org.schabi.newpipe.player.notification.NotificationPlayerUi;
@@ -52,6 +55,12 @@ public final class PlayerService extends MediaBrowserServiceCompat {
5255
public static final String SHOULD_START_FOREGROUND_EXTRA = "should_start_foreground_extra";
5356
public static final String BIND_PLAYER_HOLDER_ACTION = "bind_player_holder_action";
5457

58+
// these are instantiated in onCreate() as per
59+
// https://developer.android.com/training/cars/media#browser_workflow
60+
private MediaSessionCompat mediaSession;
61+
private MediaSessionConnector sessionConnector;
62+
63+
@Nullable
5564
private Player player;
5665

5766
private final IBinder mBinder = new PlayerService.LocalBinder(this);
@@ -71,6 +80,12 @@ public void onCreate() {
7180
assureCorrectAppLanguage(this);
7281
ThemeHelper.setTheme(this);
7382

83+
// see https://developer.android.com/training/cars/media#browser_workflow
84+
mediaSession = new MediaSessionCompat(this, "MediaSessionPlayerServ");
85+
setSessionToken(mediaSession.getSessionToken());
86+
sessionConnector = new MediaSessionConnector(mediaSession);
87+
sessionConnector.setMetadataDeduplicationEnabled(true);
88+
7489
// Note: you might be tempted to create the player instance and call startForeground here,
7590
// but be aware that the Android system might start the service just to perform media
7691
// queries. In those cases creating a player instance is a waste of resources, and calling
@@ -94,7 +109,7 @@ public int onStartCommand(final Intent intent, final int flags, final int startI
94109
if (intent.getBooleanExtra(SHOULD_START_FOREGROUND_EXTRA, false)) {
95110
if (player == null) {
96111
// make sure the player exists, in case the service was resumed
97-
player = new Player(this);
112+
player = new Player(this, mediaSession, sessionConnector);
98113
}
99114

100115
// Be sure that the player notification is set and the service is started in foreground,
@@ -159,7 +174,11 @@ public void onDestroy() {
159174
Log.d(TAG, "destroy() called");
160175
}
161176
super.onDestroy();
177+
162178
cleanup();
179+
180+
mediaSession.setActive(false);
181+
mediaSession.release();
163182
}
164183

165184
private void cleanup() {

app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ public class MediaSessionPlayerUi extends PlayerUi
3838
implements SharedPreferences.OnSharedPreferenceChangeListener {
3939
private static final String TAG = "MediaSessUi";
4040

41-
@Nullable
42-
private MediaSessionCompat mediaSession;
43-
@Nullable
44-
private MediaSessionConnector sessionConnector;
41+
@NonNull
42+
private final MediaSessionCompat mediaSession;
43+
@NonNull
44+
private final MediaSessionConnector sessionConnector;
4545

4646
private final String ignoreHardwareMediaButtonsKey;
4747
private boolean shouldIgnoreHardwareMediaButtons = false;
@@ -50,9 +50,13 @@ public class MediaSessionPlayerUi extends PlayerUi
5050
private List<NotificationActionData> prevNotificationActions = List.of();
5151

5252

53-
public MediaSessionPlayerUi(@NonNull final Player player) {
53+
public MediaSessionPlayerUi(@NonNull final Player player,
54+
@NonNull final MediaSessionCompat mediaSession,
55+
@NonNull final MediaSessionConnector sessionConnector) {
5456
super(player);
55-
ignoreHardwareMediaButtonsKey =
57+
this.mediaSession = mediaSession;
58+
this.sessionConnector = sessionConnector;
59+
this.ignoreHardwareMediaButtonsKey =
5660
context.getString(R.string.ignore_hardware_media_buttons_key);
5761
}
5862

@@ -61,10 +65,8 @@ public void initPlayer() {
6165
super.initPlayer();
6266
destroyPlayer(); // release previously used resources
6367

64-
mediaSession = new MediaSessionCompat(context, TAG);
6568
mediaSession.setActive(true);
6669

67-
sessionConnector = new MediaSessionConnector(mediaSession);
6870
sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, player));
6971
sessionConnector.setPlayer(getForwardingPlayer());
7072

@@ -89,27 +91,18 @@ public void initPlayer() {
8991
public void destroyPlayer() {
9092
super.destroyPlayer();
9193
player.getPrefs().unregisterOnSharedPreferenceChangeListener(this);
92-
if (sessionConnector != null) {
93-
sessionConnector.setMediaButtonEventHandler(null);
94-
sessionConnector.setPlayer(null);
95-
sessionConnector.setQueueNavigator(null);
96-
sessionConnector = null;
97-
}
98-
if (mediaSession != null) {
99-
mediaSession.setActive(false);
100-
mediaSession.release();
101-
mediaSession = null;
102-
}
94+
sessionConnector.setMediaButtonEventHandler(null);
95+
sessionConnector.setPlayer(null);
96+
sessionConnector.setQueueNavigator(null);
97+
mediaSession.setActive(false);
10398
prevNotificationActions = List.of();
10499
}
105100

106101
@Override
107102
public void onThumbnailLoaded(@Nullable final Bitmap bitmap) {
108103
super.onThumbnailLoaded(bitmap);
109-
if (sessionConnector != null) {
110-
// the thumbnail is now loaded: invalidate the metadata to trigger a metadata update
111-
sessionConnector.invalidateMediaSessionMetadata();
112-
}
104+
// the thumbnail is now loaded: invalidate the metadata to trigger a metadata update
105+
sessionConnector.invalidateMediaSessionMetadata();
113106
}
114107

115108

@@ -200,8 +193,8 @@ private void updateMediaSessionActions() {
200193
return;
201194
}
202195

203-
if (sessionConnector == null) {
204-
// sessionConnector will be null after destroyPlayer is called
196+
if (!mediaSession.isActive()) {
197+
// mediaSession will be inactive after destroyPlayer is called
205198
return;
206199
}
207200

0 commit comments

Comments
 (0)