question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using the PlayerNotificationManager in a foreground service

See original GitHub issue

I am using PlayerNotificationManager notification manager of exoplayer to manage the foreground notifications. I am adding/removing the notification service from my activity. Adding notification service :

          val intent = Intent(mActivity, MediaConsumptionService::class.java)
          val bundle = Bundle()
          Util.startForegroundService(mActivity, intent)

Removing notification service : mActivity.stopService(Intent(mActivity, MediaConsumptionService::class.java))

When I remove the notification service, onDestroy of service is called which is expected behaviour. But sometimes onNotificationPosted is also called after onDestroy. What are the conditions in which onNotificationPosted is called? Ideally it should not be called after onDestroy is called.

My Notification service code

class MediaConsumptionService : Service() {

    private var player: SimpleExoPlayer? = null
    var image: Bitmap? = null
    private lateinit var playerNotificationManager: PlayerNotificationManager

    private val NOTIFICATION_CHANNEL_ID = "playback_channel"
    private val NOTIFICATION_ID = 2
    private var additionalJson: String? = null
    private var trackId = 0L
    private var trackTitle = ""
    private var trackThumbUrl = ""

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        player = VideoPlayer.getInstance().player
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val context = this
        val bundle = intent?.extras
        if (bundle != null) {
            trackId = bundle.getLong(MVConstants.PLAYBACK_TRACK_ID, 0)
            trackTitle = bundle.getString(MVConstants.PLAYBACK_TRACK_TITLE, "")
            trackThumbUrl = bundle.getString(MVConstants.PLAYBACK_TRACK_THUMB, "")
            additionalJson = bundle.getString(MVConstants.MEDIA_JSON, "")
            Glide.with(context)
                    .asBitmap()
                    .load(trackThumbUrl)
                    .into(object : CustomTarget<Bitmap>() {

                        override fun onLoadFailed(errorDrawable: Drawable?) {
                            super.onLoadFailed(errorDrawable)
                            addNotificationToPlayer()
                        }

                        override fun onResourceReady(resource: Bitmap, transition:
                        Transition<in Bitmap>?) {
                            image = resource
                            addNotificationToPlayer()
                        }

                        override fun onLoadCleared(placeholder: Drawable?) {
                        }
                    })

        }


        return START_NOT_STICKY
    }

    private fun addNotificationToPlayer() {
        if (player != null) {

            playerNotificationManager = createWithNotificationChannel(
                    this,
                    NOTIFICATION_CHANNEL_ID,
                    R.string.playback,
                    0,
                    NOTIFICATION_ID,
                    object : MediaDescriptionAdapter {

                        override fun createCurrentContentIntent(player: Player?): PendingIntent? {
                            val intent = VideoPlayer.getInstance().mediaSessionIntent
                            intent.putExtra(MVConstants.MEDIA_JSON, additionalJson)
                            intent.putExtra(MVConstants.PLAYBACK_TRACK_ID, trackId)
                            intent.putExtra(MVConstants.FROM_NOTIFICATION, true)
                            return PendingIntent.getActivity(applicationContext,
                                    2, intent, PendingIntent.FLAG_UPDATE_CURRENT)
                        }

                        override fun getCurrentContentText(player: Player?): String? {
                            return ""
                        }

                        override fun getCurrentContentTitle(player: Player?): String {
                            return trackTitle
                        }

                        override fun getCurrentLargeIcon(player: Player?, callback:
                        BitmapCallback?): Bitmap? {
                            return image

                        }
                    },
                    object : NotificationListener {

                        override fun onNotificationPosted(notificationId: Int,
                                                          notification: Notification?,
                                                          ongoing: Boolean) {
                            super.onNotificationPosted(notificationId, notification, ongoing)
                            if (!ongoing) {
                                stopForeground(false)
                            } else {
                                startForeground(notificationId, notification)
                            }
                        }

                        override fun onNotificationCancelled(notificationId: Int,
                                                             dismissedByUser: Boolean) {
                            super.onNotificationCancelled(notificationId, dismissedByUser)
                            stopSelf()
                        }

                    }
            )
            // omit skip previous and next actions
            playerNotificationManager.setUseNavigationActions(false);
            // omit fast forward action by setting the increment to zero
            playerNotificationManager.setFastForwardIncrementMs(0);
            // omit rewind action by setting the increment to zero
            playerNotificationManager.setRewindIncrementMs(0);

            playerNotificationManager.setSmallIcon(R.drawable.ico_notification_wings)

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                playerNotificationManager.setColor(ResourceUtils.getColor(R.color.mva_blue))
            }

            //assign the player to it
            playerNotificationManager.setPlayer(player)
        }
    }

    override fun onTaskRemoved(rootIntent: Intent) {
        stopService()
        super.onTaskRemoved(rootIntent)
    }

    /**
     * Stop service and release the video player
     * This is only executed if we remove the app from tasks or memory is low
     */
    private fun stopService() {
        if (Util.SDK_INT >= 26) {
            stopForeground(true)
        } else {
            stopSelf()
        }
        VideoPlayer.getInstance().release()
    }

    override fun onTrimMemory(level: Int) {
        super.onTrimMemory(level)
    }

    override fun onDestroy() {
        if (::playerNotificationManager.isInitialized) {
            playerNotificationManager.setPlayer(null)
        }
     
        super.onDestroy()
    }
}

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:16 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
harshmittal29commented, Jan 2, 2020

I have narrowed down the code here

class TestService : Service() {


    private var player: SimpleExoPlayer? = null
    private val NOTIFICATION_CHANNEL_ID = "playback_channel"
    private val NOTIFICATION_ID = 2
    private var playerNotificationManager: PlayerNotificationManager? = null

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }


    override fun onCreate() {
        super.onCreate()
        player = VideoPlayer.getInstance().player
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        addNotificationToPlayer()
        return START_NOT_STICKY
    }

    private fun addNotificationToPlayer() {
        if (player != null) {

            if(playerNotificationManager == null) {
                playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
                        this,
                        NOTIFICATION_CHANNEL_ID,
                        R.string.playback,
                        0,
                        NOTIFICATION_ID,
                        object : PlayerNotificationManager.MediaDescriptionAdapter {

                            override fun createCurrentContentIntent(player: Player): PendingIntent? {
                                // return pending intent
                                return null
                            }

                            override fun getCurrentContentText(player: Player): String? {
                                return ""
                            }

                            override fun getCurrentContentTitle(player: Player): String {
                                return "Title"
                            }

                            override fun getCurrentLargeIcon(player: Player, callback: PlayerNotificationManager.BitmapCallback): Bitmap? {
                                return null
                            }
                        },
                        object : PlayerNotificationManager.NotificationListener {

                            override fun onNotificationPosted(notificationId: Int,
                                                              notification: Notification,
                                                              ongoing: Boolean) {
                                super.onNotificationPosted(notificationId, notification, ongoing)
                                if (!ongoing) {
                                    stopForeground(false)
                                } else {
                                    startForeground(notificationId, notification)
                                }

                            }

                            override fun onNotificationCancelled(notificationId: Int,
                                                                 dismissedByUser: Boolean) {
                                super.onNotificationCancelled(notificationId, dismissedByUser)
                                stopSelf()
                            }

                        }
                )
                playerNotificationManager?.setPlayer(player)
            }
        }

    }

    override fun onDestroy() {
        playerNotificationManager?.setPlayer(null)
        playerNotificationManager = null
        super.onDestroy()
    }
}

I would like to ask what is the best way to call startForeground for the PlayerNotificationManager and how can I create my own NotificationBuilder and bind it to PlayerNotificationManager?

0reactions
marcbaechingercommented, Mar 16, 2020

Closing due to inactivity. Please feel free to re-open if required.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to send silent foreground notification using Exoplayer's ...
1). I create my foreground service notification using the code below but it comes with a notification sound. How can I prevent this...
Read more >
Foreground services - Android Developers
This document describes the required permission for using foreground services, how to start a foreground service and remove it from the background, how...
Read more >
Playback Notifications with ExoPlayer | by Marc Bächinger
Displaying a notification with playback controls is a best practice for media apps on Android. For audio playback in the background it's ...
Read more >
Make Music Player | Player Service and Notification | part 3
Make Music Player | Player Service and Notification | part 3 ... and Using Services in Android: Background & Foreground Services. Codeible.
Read more >
com.google.android.exoplayer2.ui.PlayerNotificationManager ...
Best Java code snippets using com.google.android.exoplayer2.ui. ... When created this notification will move the service to * foreground to avoid being ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found