diff --git a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt index 40c2e8cbc..a2cbf2544 100644 --- a/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/fragments/PlayerFragment.kt @@ -1,7 +1,6 @@ package com.github.libretube.fragments import android.app.ActivityManager -import android.app.NotificationManager import android.app.PictureInPictureParams import android.content.Context import android.content.Intent @@ -16,7 +15,6 @@ import android.os.Bundle import android.os.Handler import android.os.Looper import android.os.PowerManager -import android.support.v4.media.session.MediaSessionCompat import android.text.Html import android.text.format.DateUtils import android.util.Log @@ -35,9 +33,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import com.fasterxml.jackson.databind.ObjectMapper -import com.github.libretube.BACKGROUND_CHANNEL_ID import com.github.libretube.Globals -import com.github.libretube.PLAYER_NOTIFICATION_ID import com.github.libretube.R import com.github.libretube.activities.MainActivity import com.github.libretube.adapters.ChaptersAdapter @@ -50,17 +46,17 @@ import com.github.libretube.dialogs.AddToPlaylistDialog import com.github.libretube.dialogs.DownloadDialog import com.github.libretube.dialogs.ShareDialog import com.github.libretube.obj.ChapterSegment -import com.github.libretube.obj.Playlist import com.github.libretube.obj.Segment import com.github.libretube.obj.Segments import com.github.libretube.obj.Streams import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceKeys import com.github.libretube.services.BackgroundMode +import com.github.libretube.util.AutoPlayHelper import com.github.libretube.util.BackgroundHelper import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.CronetHelper -import com.github.libretube.util.DescriptionAdapter +import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.OnDoubleTapEventListener import com.github.libretube.util.PlayerHelper import com.github.libretube.util.RetrofitInstance @@ -77,7 +73,6 @@ import com.google.android.exoplayer2.MediaItem.fromUri import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.cronet.CronetDataSource -import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.source.DefaultMediaSourceFactory import com.google.android.exoplayer2.source.MediaSource import com.google.android.exoplayer2.source.MergingMediaSource @@ -85,7 +80,6 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.ui.AspectRatioFrameLayout import com.google.android.exoplayer2.ui.CaptionStyleCompat -import com.google.android.exoplayer2.ui.PlayerNotificationManager import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.ui.TimeBar import com.google.android.exoplayer2.upstream.DataSource @@ -175,15 +169,12 @@ class PlayerFragment : Fragment() { * for autoplay */ private var nextStreamId: String? = null - private var playlistStreamIds: MutableList = arrayListOf() - private var playlistNextPage: String? = null + private lateinit var autoPlayHelper: AutoPlayHelper /** * for the player notification */ - private lateinit var mediaSession: MediaSessionCompat - private lateinit var mediaSessionConnector: MediaSessionConnector - private lateinit var playerNotification: PlayerNotificationManager + private lateinit var nowPlayingNotification: NowPlayingNotification override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -676,15 +667,7 @@ class PlayerFragment : Fragment() { super.onDestroy() try { saveWatchPosition() - mediaSession.isActive = false - mediaSession.release() - mediaSessionConnector.setPlayer(null) - playerNotification.setPlayer(null) - val notificationManager = context?.getSystemService( - Context.NOTIFICATION_SERVICE - ) as NotificationManager - notificationManager.cancel(1) - exoPlayer.release() + nowPlayingNotification.destroy() activity?.requestedOrientation = if ((activity as MainActivity).autoRotationEnabled) ActivityInfo.SCREEN_ORIENTATION_USER else ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT @@ -752,12 +735,12 @@ class PlayerFragment : Fragment() { exoPlayer.prepare() exoPlayer.play() exoPlayerView.useController = true - initializePlayerNotification(requireContext()) + initializePlayerNotification() if (sponsorBlockEnabled) fetchSponsorBlockSegments() // show comments if related streams disabled if (!relatedStreamsEnabled) toggleComments() // prepare for autoplay - initAutoPlay() + if (autoplayEnabled) setNextStream() if (watchHistoryEnabled) { PreferenceHelper.addToWatchHistory(videoId!!, streams) } @@ -767,6 +750,20 @@ class PlayerFragment : Fragment() { run() } + /** + * set the videoId of the next stream for autoplay + */ + private fun setNextStream() { + nextStreamId = streams.relatedStreams!![0].url.toID() + if (playlistId == null) return + if (!this::autoPlayHelper.isInitialized) autoPlayHelper = AutoPlayHelper(playlistId!!) + // search for the next videoId in the playlist + lifecycleScope.launchWhenCreated { + val nextId = autoPlayHelper.getNextPlaylistVideoId(videoId!!) + if (nextId != null) nextStreamId = nextId + } + } + /** * fetch the segments for SponsorBlock */ @@ -824,59 +821,6 @@ class PlayerFragment : Fragment() { if (position != null) exoPlayer.seekTo(position!!) } - // the function is working recursively - private fun initAutoPlay() { - // save related streams for autoplay - if (autoplayEnabled) { - // if it's a playlist use the next video - if (playlistId != null) { - lateinit var playlist: Playlist // var for saving the list in - // runs only the first time when starting a video from a playlist - if (playlistStreamIds.isEmpty()) { - CoroutineScope(Dispatchers.IO).launch { - // fetch the playlists videos - playlist = RetrofitInstance.api.getPlaylist(playlistId!!) - // save the playlist urls in the array - playlist.relatedStreams?.forEach { video -> - playlistStreamIds += video.url.toID() - } - // save playlistNextPage for usage if video is not contained - playlistNextPage = playlist.nextpage - // restart the function after videos are loaded - initAutoPlay() - } - } - // if the playlists contain the video, then save the next video as next stream - else if (playlistStreamIds.contains(videoId)) { - val index = playlistStreamIds.indexOf(videoId) - // check whether there's a next video - if (index + 1 <= playlistStreamIds.size) { - nextStreamId = playlistStreamIds[index + 1] - } - // fetch the next page of the playlist if the video isn't contained - } else if (playlistNextPage != null) { - CoroutineScope(Dispatchers.IO).launch { - RetrofitInstance.api.getPlaylistNextPage(playlistId!!, playlistNextPage!!) - // append all the playlist item urls to the array - playlist.relatedStreams?.forEach { video -> - playlistStreamIds += video.url.toID() - } - // save playlistNextPage for usage if video is not contained - playlistNextPage = playlist.nextpage - // restart the function after videos are loaded - initAutoPlay() - } - } - // else: the video must be the last video of the playlist so nothing happens - - // if it's not a playlist then use the next related video - } else if (streams.relatedStreams != null && streams.relatedStreams!!.isNotEmpty()) { - // save next video from related streams for autoplay - nextStreamId = streams.relatedStreams!![0].url.toID() - } - } - } - // used for autoplay and skipping to next video private fun playNextVideo() { // check whether there is a new video in the queue @@ -1502,33 +1446,14 @@ class PlayerFragment : Fragment() { exoPlayer.setAudioAttributes(audioAttributes, true) } - private fun initializePlayerNotification(c: Context) { - mediaSession = MediaSessionCompat(c, this.javaClass.name) - mediaSession.apply { - isActive = true - } - - mediaSessionConnector = MediaSessionConnector(mediaSession) - mediaSessionConnector.setPlayer(exoPlayer) - - playerNotification = PlayerNotificationManager - .Builder(c, PLAYER_NOTIFICATION_ID, BACKGROUND_CHANNEL_ID) - .setMediaDescriptionAdapter( - DescriptionAdapter( - streams.title!!, - streams.uploader!!, - streams.thumbnailUrl!!, - requireContext() - ) - ) - .build() - - playerNotification.apply { - setPlayer(exoPlayer) - setUsePreviousAction(false) - setUseStopAction(true) - setMediaSessionToken(mediaSession.sessionToken) + /** + * show the [NowPlayingNotification] for the current video + */ + private fun initializePlayerNotification() { + if (!this::nowPlayingNotification.isInitialized) { + nowPlayingNotification = NowPlayingNotification(requireContext(), exoPlayer) } + nowPlayingNotification.updatePlayerNotification(streams) } // lock the player diff --git a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt index 0431263e8..28fefb948 100644 --- a/app/src/main/java/com/github/libretube/services/BackgroundMode.kt +++ b/app/src/main/java/com/github/libretube/services/BackgroundMode.kt @@ -4,13 +4,11 @@ import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.Service -import android.content.Context import android.content.Intent import android.os.Build import android.os.Handler import android.os.IBinder import android.os.Looper -import android.support.v4.media.session.MediaSessionCompat import com.fasterxml.jackson.databind.ObjectMapper import com.github.libretube.BACKGROUND_CHANNEL_ID import com.github.libretube.PLAYER_NOTIFICATION_ID @@ -20,7 +18,7 @@ import com.github.libretube.obj.Segments import com.github.libretube.obj.Streams import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceKeys -import com.github.libretube.util.DescriptionAdapter +import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PlayerHelper import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.toID @@ -30,7 +28,6 @@ import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector -import com.google.android.exoplayer2.ui.PlayerNotificationManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -56,21 +53,6 @@ class BackgroundMode : Service() { private var player: ExoPlayer? = null private var playWhenReadyPlayer = true - /** - * The [MediaSessionCompat] for the [response]. - */ - private lateinit var mediaSession: MediaSessionCompat - - /** - * The [MediaSessionConnector] to connect with the [mediaSession] and implement it with the [player]. - */ - private lateinit var mediaSessionConnector: MediaSessionConnector - - /** - * The [PlayerNotificationManager] to load the [mediaSession] content on it. - */ - private var playerNotification: PlayerNotificationManager? = null - /** * The [AudioAttributes] handle the audio focus of the [player] */ @@ -81,11 +63,16 @@ class BackgroundMode : Service() { */ private var segmentData: Segments? = null + /** + * Notification for the player + */ + private lateinit var nowPlayingNotification: NowPlayingNotification + + /** + * Setting the required [notification] for running as a foreground service + */ override fun onCreate() { super.onCreate() - /** - * setting the required notification for running as a foreground service - */ if (Build.VERSION.SDK_INT >= 26) { val channelId = BACKGROUND_CHANNEL_ID val channel = NotificationChannel( @@ -106,15 +93,17 @@ class BackgroundMode : Service() { * Initializes the [player] with the [MediaItem]. */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - // destroy the old player - destroyPlayer() + try { + // get the intent arguments + videoId = intent?.getStringExtra("videoId")!! + val position = intent.getLongExtra("position", 0L) - // get the intent arguments - videoId = intent?.getStringExtra("videoId")!! - val position = intent.getLongExtra("position", 0L) - - // play the audio in the background - playAudio(videoId, position) + // play the audio in the background + playAudio(videoId, position) + } catch (e: Exception) { + stopForeground(true) + stopSelf() + } return super.onStartCommand(intent, flags, startId) } @@ -133,7 +122,13 @@ class BackgroundMode : Service() { job.join() initializePlayer() - initializePlayerNotification() + setMediaItem() + + // create the notification + if (!this@BackgroundMode::nowPlayingNotification.isInitialized) { + nowPlayingNotification = NowPlayingNotification(this@BackgroundMode, player!!) + } + nowPlayingNotification.updatePlayerNotification(response!!) player?.apply { playWhenReady = playWhenReadyPlayer @@ -174,16 +169,11 @@ class BackgroundMode : Service() { if (autoplay) playNextVideo() } Player.STATE_IDLE -> { - // called when the user pressed stop in the notification - // stop the service from being in the foreground and remove the notification - stopForeground(true) - // destroy the service - stopSelf() + onDestroy() } } } }) - setMediaItem() } /** @@ -194,9 +184,6 @@ class BackgroundMode : Service() { val videoId = response!! .relatedStreams!![0].url.toID() - // destroy previous notification and player - destroyPlayer() - // play new video on background this.videoId = videoId this.segmentData = null @@ -204,32 +191,6 @@ class BackgroundMode : Service() { } } - /** - * Initializes the [playerNotification] attached to the [player] and shows it. - */ - private fun initializePlayerNotification() { - playerNotification = PlayerNotificationManager - .Builder(this, PLAYER_NOTIFICATION_ID, BACKGROUND_CHANNEL_ID) - // set the description of the notification - .setMediaDescriptionAdapter( - DescriptionAdapter( - response?.title!!, - response?.uploader!!, - response?.thumbnailUrl!!, - this - ) - ) - .build() - playerNotification?.apply { - setPlayer(player) - setUseNextAction(false) - setUsePreviousAction(false) - setUseStopAction(true) - setColorized(true) - setMediaSessionToken(mediaSession.sessionToken) - } - } - /** * Sets the [MediaItem] with the [response] into the [player]. Also creates a [MediaSessionConnector] * with the [mediaSession] and attach it to the [player]. @@ -239,12 +200,6 @@ class BackgroundMode : Service() { val mediaItem = MediaItem.Builder().setUri(it.hls!!).build() player?.setMediaItem(mediaItem) } - - mediaSession = MediaSessionCompat(this, this.javaClass.name) - mediaSession.isActive = true - - mediaSessionConnector = MediaSessionConnector(mediaSession) - mediaSessionConnector.setPlayer(player) } /** @@ -284,15 +239,17 @@ class BackgroundMode : Service() { } } - private fun destroyPlayer() { - // clear old player and its notification - playerNotification = null - player = null - - // kill old notification - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) - as NotificationManager - notificationManager.cancel(PLAYER_NOTIFICATION_ID) + /** + * destroy the [BackgroundMode] foreground service + */ + override fun onDestroy() { + // called when the user pressed stop in the notification + // stop the service from being in the foreground and remove the notification + stopForeground(true) + // destroy the service + stopSelf() + if (this::nowPlayingNotification.isInitialized) nowPlayingNotification.destroy() + super.onDestroy() } override fun onBind(p0: Intent?): IBinder? { diff --git a/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt b/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt new file mode 100644 index 000000000..c1e82a491 --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/AutoPlayHelper.kt @@ -0,0 +1,37 @@ +package com.github.libretube.util + +import com.github.libretube.obj.Playlist +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class AutoPlayHelper( + private val playlistId: String +) { + private val TAG = "AutoPlayHelper" + + private val playlistStreamIds = mutableListOf() + private lateinit var playlist: Playlist + private var playlistNextPage: String? = null + + suspend fun getNextPlaylistVideoId(currentVideoId: String): String? { + // if the playlists contain the video, then save the next video as next stream + if (playlistStreamIds.contains(currentVideoId)) { + val index = playlistStreamIds.indexOf(currentVideoId) + // check whether there's a next video + return if (index < playlistStreamIds.size) playlistStreamIds[index + 1] + else getNextPlaylistVideoId(currentVideoId) + } else if (playlistStreamIds.isEmpty() || playlistNextPage != null) { + // fetch the next page of the playlist + return withContext(Dispatchers.IO) { + // fetch the playlists videos + playlist = RetrofitInstance.api.getPlaylist(playlistId) + // save the playlist urls in the array + playlistStreamIds += playlist.relatedStreams!!.map { it.url.toID() } + // save playlistNextPage for usage if video is not contained + playlistNextPage = playlist.nextpage + return@withContext getNextPlaylistVideoId(currentVideoId) + } + } + return null + } +} diff --git a/app/src/main/java/com/github/libretube/util/DescriptionAdapter.kt b/app/src/main/java/com/github/libretube/util/DescriptionAdapter.kt deleted file mode 100644 index 6bc27bf40..000000000 --- a/app/src/main/java/com/github/libretube/util/DescriptionAdapter.kt +++ /dev/null @@ -1,95 +0,0 @@ -package com.github.libretube.util - -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import com.github.libretube.activities.MainActivity -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.ui.PlayerNotificationManager -import java.net.URL - -/** - * The [DescriptionAdapter] is used to show title, uploaderName and thumbnail of the video in the notification - * Basic example [here](https://github.com/AnthonyMarkD/AudioPlayerSampleTest) - */ -class DescriptionAdapter( - private val title: String, - private val channelName: String, - private val thumbnailUrl: String, - private val context: Context -) : - PlayerNotificationManager.MediaDescriptionAdapter { - /** - * sets the title of the notification - */ - override fun getCurrentContentTitle(player: Player): CharSequence { - // return controller.metadata.description.title.toString() - return title - } - - /** - * overrides the action when clicking the notification - */ - override fun createCurrentContentIntent(player: Player): PendingIntent? { - // return controller.sessionActivity - /** - * starts a new MainActivity Intent when the player notification is clicked - * it doesn't start a completely new MainActivity because the MainActivity's launchMode - * is set to "singleTop" in the AndroidManifest (important!!!) - * that's the only way to launch back into the previous activity (e.g. the player view - */ - val intent = Intent(context, MainActivity::class.java) - return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) - } - - /** - * the description of the notification (below the title) - */ - override fun getCurrentContentText(player: Player): CharSequence? { - // return controller.metadata.description.subtitle.toString() - return channelName - } - - /** - * return the icon/thumbnail of the video - */ - override fun getCurrentLargeIcon( - player: Player, - callback: PlayerNotificationManager.BitmapCallback - ): Bitmap? { - lateinit var bitmap: Bitmap - - /** - * running on a new thread to prevent a NetworkMainThreadException - */ - val thread = Thread { - try { - /** - * try to GET the thumbnail from the URL - */ - val inputStream = URL(thumbnailUrl).openStream() - bitmap = BitmapFactory.decodeStream(inputStream) - } catch (ex: java.lang.Exception) { - ex.printStackTrace() - } - } - thread.start() - thread.join() - /** - * returns the scaled bitmap if it got fetched successfully - */ - return try { - val resizedBitmap = Bitmap.createScaledBitmap( - bitmap, - bitmap.width, - bitmap.width, - false - ) - resizedBitmap - } catch (e: Exception) { - null - } - } -} diff --git a/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt new file mode 100644 index 000000000..f0f52809c --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/NowPlayingNotification.kt @@ -0,0 +1,180 @@ +package com.github.libretube.util + +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.support.v4.media.session.MediaSessionCompat +import com.github.libretube.BACKGROUND_CHANNEL_ID +import com.github.libretube.PLAYER_NOTIFICATION_ID +import com.github.libretube.activities.MainActivity +import com.github.libretube.obj.Streams +import com.google.android.exoplayer2.ExoPlayer +import com.google.android.exoplayer2.Player +import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector +import com.google.android.exoplayer2.ui.PlayerNotificationManager +import java.net.URL + +class NowPlayingNotification( + private val context: Context, + private val player: ExoPlayer +) { + private var streams: Streams? = null + + /** + * The [MediaSessionCompat] for the [streams]. + */ + private lateinit var mediaSession: MediaSessionCompat + + /** + * The [MediaSessionConnector] to connect with the [mediaSession] and implement it with the [player]. + */ + private lateinit var mediaSessionConnector: MediaSessionConnector + + /** + * The [PlayerNotificationManager] to load the [mediaSession] content on it. + */ + private var playerNotification: PlayerNotificationManager? = null + + /** + * The [DescriptionAdapter] is used to show title, uploaderName and thumbnail of the video in the notification + * Basic example [here](https://github.com/AnthonyMarkD/AudioPlayerSampleTest) + */ + inner class DescriptionAdapter() : + PlayerNotificationManager.MediaDescriptionAdapter { + /** + * sets the title of the notification + */ + override fun getCurrentContentTitle(player: Player): CharSequence { + // return controller.metadata.description.title.toString() + return streams?.title!! + } + + /** + * overrides the action when clicking the notification + */ + override fun createCurrentContentIntent(player: Player): PendingIntent? { + // return controller.sessionActivity + /** + * starts a new MainActivity Intent when the player notification is clicked + * it doesn't start a completely new MainActivity because the MainActivity's launchMode + * is set to "singleTop" in the AndroidManifest (important!!!) + * that's the only way to launch back into the previous activity (e.g. the player view + */ + val intent = Intent(context, MainActivity::class.java) + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + } + + /** + * the description of the notification (below the title) + */ + override fun getCurrentContentText(player: Player): CharSequence? { + // return controller.metadata.description.subtitle.toString() + return streams?.uploader + } + + /** + * return the icon/thumbnail of the video + */ + override fun getCurrentLargeIcon( + player: Player, + callback: PlayerNotificationManager.BitmapCallback + ): Bitmap? { + lateinit var bitmap: Bitmap + + /** + * running on a new thread to prevent a NetworkMainThreadException + */ + val thread = Thread { + try { + /** + * try to GET the thumbnail from the URL + */ + val inputStream = URL(streams?.thumbnailUrl).openStream() + bitmap = BitmapFactory.decodeStream(inputStream) + } catch (ex: java.lang.Exception) { + ex.printStackTrace() + } + } + thread.start() + thread.join() + /** + * returns the scaled bitmap if it got fetched successfully + */ + return try { + val resizedBitmap = Bitmap.createScaledBitmap( + bitmap, + bitmap.width, + bitmap.width, + false + ) + resizedBitmap + } catch (e: Exception) { + null + } + } + } + + /** + * Creates a [MediaSessionCompat] amd a [MediaSessionConnector] for the player + */ + private fun createMediaSession() { + if (this::mediaSession.isInitialized) return + mediaSession = MediaSessionCompat(context, this.javaClass.name) + mediaSession.isActive = true + + mediaSessionConnector = MediaSessionConnector(mediaSession) + mediaSessionConnector.setPlayer(player) + } + + /** + * Updates or creates the [playerNotification] + */ + fun updatePlayerNotification( + streams: Streams + ) { + this.streams = streams + if (playerNotification == null) { + createMediaSession() + createNotification() + } + } + + /** + * Initializes the [playerNotification] attached to the [player] and shows it. + */ + private fun createNotification() { + playerNotification = PlayerNotificationManager + .Builder(context, PLAYER_NOTIFICATION_ID, BACKGROUND_CHANNEL_ID) + // set the description of the notification + .setMediaDescriptionAdapter( + DescriptionAdapter() + ) + .build() + playerNotification?.apply { + setPlayer(player) + setUseNextAction(false) + setUsePreviousAction(false) + setUseStopAction(true) + setColorized(true) + setMediaSessionToken(mediaSession.sessionToken) + } + } + + /** + * Destroy the [NowPlayingNotification] + */ + fun destroy() { + mediaSession.isActive = false + mediaSession.release() + mediaSessionConnector.setPlayer(null) + playerNotification?.setPlayer(null) + val notificationManager = context.getSystemService( + Context.NOTIFICATION_SERVICE + ) as NotificationManager + notificationManager.cancel(PLAYER_NOTIFICATION_ID) + player.release() + } +}