From c73763d2c3cda7157628223ec3719a1d0386e195 Mon Sep 17 00:00:00 2001 From: Haggai Eran Date: Mon, 2 Jan 2023 21:43:28 +0200 Subject: [PATCH] Keep MediaSessionCompat and MediaSessionConnector in a separate class These objects need to live beyond the player for supporting MediaBrowserServiceCompat and Android Auto, so they need to move outside of the MediaSessionPlayerUi class. --- .../org/schabi/newpipe/player/Player.java | 2 +- .../schabi/newpipe/player/PlayerService.java | 46 +++++++++++++------ .../mediabrowser/MediaBrowserConnector.java | 32 +++++++++++++ .../mediasession/MediaSessionPlayerUi.java | 36 +++++++-------- 4 files changed, 81 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserConnector.java diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 920435a7e..dcf97785a 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -302,7 +302,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, service.getSessionConnector()), new NotificationPlayerUi(this) ); } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayerService.java b/app/src/main/java/org/schabi/newpipe/player/PlayerService.java index e7abf4320..e460f608f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayerService.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayerService.java @@ -28,6 +28,9 @@ import android.os.Binder; import android.os.IBinder; import android.util.Log; +import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; + +import org.schabi.newpipe.player.mediabrowser.MediaBrowserConnector; import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; import org.schabi.newpipe.player.notification.NotificationPlayerUi; import org.schabi.newpipe.util.ThemeHelper; @@ -47,6 +50,9 @@ public final class PlayerService extends Service { private final IBinder mBinder = new PlayerService.LocalBinder(this); + private MediaBrowserConnector mediaBrowserConnector; + + /*////////////////////////////////////////////////////////////////////////// // Service's LifeCycle //////////////////////////////////////////////////////////////////////////*/ @@ -59,15 +65,21 @@ public final class PlayerService extends Service { assureCorrectAppLanguage(this); ThemeHelper.setTheme(this); - player = new Player(this); - /* - Create the player notification and start immediately the service in foreground, - otherwise if nothing is played or initializing the player and its components (especially - loading stream metadata) takes a lot of time, the app would crash on Android 8+ as the - service would never be put in the foreground while we said to the system we would do so - */ - player.UIs().get(NotificationPlayerUi.class) - .ifPresent(NotificationPlayerUi::createNotificationAndStartForeground); + mediaBrowserConnector = new MediaBrowserConnector(this); + } + + private void initializePlayer() { + if (player == null) { + player = new Player(this); + /* + Create the player notification and start immediately the service in foreground, + otherwise if nothing is played or initializing the player and its components (especially + loading stream metadata) takes a lot of time, the app would crash on Android 8+ as the + service would never be put in the foreground while we said to the system we would do so + */ + player.UIs().get(NotificationPlayerUi.class) + .ifPresent(NotificationPlayerUi::createNotificationAndStartForeground); + } } @Override @@ -104,11 +116,10 @@ public final class PlayerService extends Service { return START_NOT_STICKY; } - if (player != null) { - player.handleIntent(intent); - player.UIs().get(MediaSessionPlayerUi.class) - .ifPresent(ui -> ui.handleMediaButtonIntent(intent)); - } + initializePlayer(); + player.handleIntent(intent); + player.UIs().get(MediaSessionPlayerUi.class) + .ifPresent(ui -> ui.handleMediaButtonIntent(intent)); return START_NOT_STICKY; } @@ -143,6 +154,10 @@ public final class PlayerService extends Service { Log.d(TAG, "destroy() called"); } cleanup(); + if (mediaBrowserConnector != null) { + mediaBrowserConnector.release(); + mediaBrowserConnector = null; + } } private void cleanup() { @@ -167,6 +182,9 @@ public final class PlayerService extends Service { return mBinder; } + public MediaSessionConnector getSessionConnector() { + return mediaBrowserConnector.getSessionConnector(); + } public static class LocalBinder extends Binder { private final WeakReference playerService; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserConnector.java b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserConnector.java new file mode 100644 index 000000000..6fc61c3d7 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserConnector.java @@ -0,0 +1,32 @@ +package org.schabi.newpipe.player.mediabrowser; + +import android.support.v4.media.session.MediaSessionCompat; + +import androidx.annotation.NonNull; + +import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; + +import org.schabi.newpipe.player.PlayerService; + +public class MediaBrowserConnector { + private static final String TAG = MediaBrowserConnector.class.getSimpleName(); + + private final PlayerService playerService; + private final @NonNull MediaSessionConnector sessionConnector; + private final @NonNull MediaSessionCompat mediaSession; + + public MediaBrowserConnector(@NonNull final PlayerService playerService) { + this.playerService = playerService; + mediaSession = new MediaSessionCompat(playerService, TAG); + sessionConnector = new MediaSessionConnector(mediaSession); + sessionConnector.setMetadataDeduplicationEnabled(true); + } + + public @NonNull MediaSessionConnector getSessionConnector() { + return sessionConnector; + } + + public void release() { + mediaSession.release(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java index c673e688c..8b69db82f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java @@ -50,8 +50,11 @@ public class MediaSessionPlayerUi extends PlayerUi private List prevNotificationActions = List.of(); - public MediaSessionPlayerUi(@NonNull final Player player) { + public MediaSessionPlayerUi(@NonNull final Player player, + @NonNull final MediaSessionConnector sessionConnector) { super(player); + this.mediaSession = sessionConnector.mediaSession; + this.sessionConnector = sessionConnector; ignoreHardwareMediaButtonsKey = context.getString(R.string.ignore_hardware_media_buttons_key); } @@ -61,10 +64,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()); @@ -77,7 +78,6 @@ public class MediaSessionPlayerUi extends PlayerUi updateShouldIgnoreHardwareMediaButtons(player.getPrefs()); player.getPrefs().registerOnSharedPreferenceChangeListener(this); - sessionConnector.setMetadataDeduplicationEnabled(true); sessionConnector.setMediaMetadataProvider(exoPlayer -> buildMediaMetadata()); // force updating media session actions by resetting the previous ones @@ -89,27 +89,23 @@ 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); + sessionConnector.setMediaMetadataProvider(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(); } @@ -132,7 +128,7 @@ public class MediaSessionPlayerUi extends PlayerUi } public Optional getSessionToken() { - return Optional.ofNullable(mediaSession).map(MediaSessionCompat::getSessionToken); + return Optional.of(mediaSession.getSessionToken()); }