11package org .schabi .newpipe .player .mediasession ;
22
3+ import static org .schabi .newpipe .MainActivity .DEBUG ;
4+
35import android .content .Intent ;
46import android .graphics .Bitmap ;
7+ import android .support .v4 .media .MediaMetadataCompat ;
58import android .support .v4 .media .session .MediaSessionCompat ;
9+ import android .util .Log ;
610
711import androidx .annotation .NonNull ;
812import androidx .annotation .Nullable ;
13+ import androidx .media .session .MediaButtonReceiver ;
14+
15+ import com .google .android .exoplayer2 .ForwardingPlayer ;
16+ import com .google .android .exoplayer2 .ext .mediasession .MediaSessionConnector ;
917
18+ import org .schabi .newpipe .R ;
1019import org .schabi .newpipe .player .Player ;
1120import org .schabi .newpipe .player .ui .PlayerUi ;
21+ import org .schabi .newpipe .player .ui .VideoPlayerUi ;
22+ import org .schabi .newpipe .util .StreamTypeUtil ;
1223
1324import java .util .Optional ;
1425
1526public class MediaSessionPlayerUi extends PlayerUi {
27+ private static final String TAG = "MediaSessUi" ;
1628
17- private MediaSessionManager mediaSessionManager ;
29+ private MediaSessionCompat mediaSession ;
30+ private MediaSessionConnector sessionConnector ;
1831
1932 public MediaSessionPlayerUi (@ NonNull final Player player ) {
2033 super (player );
@@ -23,18 +36,31 @@ public MediaSessionPlayerUi(@NonNull final Player player) {
2336 @ Override
2437 public void initPlayer () {
2538 super .initPlayer ();
26- if (mediaSessionManager != null ) {
27- mediaSessionManager .dispose ();
28- }
29- mediaSessionManager = new MediaSessionManager (context , player );
39+ destroyPlayer (); // release previously used resources
40+
41+ mediaSession = new MediaSessionCompat (context , TAG );
42+ mediaSession .setActive (true );
43+
44+ sessionConnector = new MediaSessionConnector (mediaSession );
45+ sessionConnector .setQueueNavigator (new PlayQueueNavigator (mediaSession , player ));
46+ sessionConnector .setPlayer (getForwardingPlayer ());
47+
48+ sessionConnector .setMetadataDeduplicationEnabled (true );
49+ sessionConnector .setMediaMetadataProvider (exoPlayer -> buildMediaMetadata ());
3050 }
3151
3252 @ Override
3353 public void destroyPlayer () {
3454 super .destroyPlayer ();
35- if (mediaSessionManager != null ) {
36- mediaSessionManager .dispose ();
37- mediaSessionManager = null ;
55+ if (sessionConnector != null ) {
56+ sessionConnector .setPlayer (null );
57+ sessionConnector .setQueueNavigator (null );
58+ sessionConnector = null ;
59+ }
60+ if (mediaSession != null ) {
61+ mediaSession .setActive (false );
62+ mediaSession .release ();
63+ mediaSession = null ;
3864 }
3965 }
4066
@@ -47,18 +73,68 @@ public void onBroadcastReceived(final Intent intent) {
4773 @ Override
4874 public void onThumbnailLoaded (@ Nullable final Bitmap bitmap ) {
4975 super .onThumbnailLoaded (bitmap );
50- if (mediaSessionManager != null ) {
51- mediaSessionManager .triggerMetadataUpdate ();
76+ if (sessionConnector != null ) {
77+ // the thumbnail is now loaded: invalidate the metadata to trigger a metadata update
78+ sessionConnector .invalidateMediaSessionMetadata ();
5279 }
5380 }
5481
82+
5583 public void handleMediaButtonIntent (final Intent intent ) {
56- if (mediaSessionManager != null ) {
57- mediaSessionManager .handleMediaButtonIntent (intent );
58- }
84+ MediaButtonReceiver .handleIntent (mediaSession , intent );
5985 }
6086
6187 public Optional <MediaSessionCompat .Token > getSessionToken () {
62- return Optional .ofNullable (mediaSessionManager ).map (MediaSessionManager ::getSessionToken );
88+ return Optional .ofNullable (mediaSession ).map (MediaSessionCompat ::getSessionToken );
89+ }
90+
91+
92+ private ForwardingPlayer getForwardingPlayer () {
93+ // ForwardingPlayer means that all media session actions called on this player are
94+ // forwarded directly to the connected exoplayer, except for the overridden methods. So
95+ // override play and pause since our player adds more functionality to them over exoplayer.
96+ return new ForwardingPlayer (player .getExoPlayer ()) {
97+ @ Override
98+ public void play () {
99+ player .play ();
100+ // hide the player controls even if the play command came from the media session
101+ player .UIs ().get (VideoPlayerUi .class ).ifPresent (ui -> ui .hideControls (0 , 0 ));
102+ }
103+
104+ @ Override
105+ public void pause () {
106+ player .pause ();
107+ }
108+ };
109+ }
110+
111+ private MediaMetadataCompat buildMediaMetadata () {
112+ if (DEBUG ) {
113+ Log .d (TAG , "buildMediaMetadata called" );
114+ }
115+
116+ // set title and artist
117+ final MediaMetadataCompat .Builder builder = new MediaMetadataCompat .Builder ()
118+ .putString (MediaMetadataCompat .METADATA_KEY_TITLE , player .getVideoTitle ())
119+ .putString (MediaMetadataCompat .METADATA_KEY_ARTIST , player .getUploaderName ());
120+
121+ // set duration (-1 for livestreams or if unknown, see the METADATA_KEY_DURATION docs)
122+ final long duration = player .getCurrentStreamInfo ()
123+ .filter (info -> !StreamTypeUtil .isLiveStream (info .getStreamType ()))
124+ .map (info -> info .getDuration () * 1000L )
125+ .orElse (-1L );
126+ builder .putLong (MediaMetadataCompat .METADATA_KEY_DURATION , duration );
127+
128+ // set album art, unless the user asked not to, or there is no thumbnail available
129+ final boolean showThumbnail = player .getPrefs ().getBoolean (
130+ context .getString (R .string .show_thumbnail_key ), true );
131+ Optional .ofNullable (player .getThumbnail ())
132+ .filter (bitmap -> showThumbnail )
133+ .ifPresent (bitmap -> {
134+ builder .putBitmap (MediaMetadataCompat .METADATA_KEY_ALBUM_ART , bitmap );
135+ builder .putBitmap (MediaMetadataCompat .METADATA_KEY_DISPLAY_ICON , bitmap );
136+ });
137+
138+ return builder .build ();
63139 }
64140}
0 commit comments