3

Spotify and Google Music allows a user to play music in background allowing also the media notification to be dismissible. When you move one of these apps to background, then pause the playback, you are able to dismiss the media notification. However if you don't dismiss the notification, so it is left in paused state for long time, the System won't kill the "audio service" as the notification is always visible and immediately react to play/pause.

As far as I know it is not possible using foreground services as in order to dismiss the notification, we'd need to call stopForeground(false), which would let system to kill the service.

Here is how we do it right now, in our app:

When playback is started, we run MusicService as a foreground service with following code:

startForeground(mediaNotification)

Then, when user leave the app, he is able to control the playback via media notification. As the notification is tightly coupled with the MusicService, user won't be able to dismiss it until we call:

stopForeground(false)

That's why we call this method when user press pause button. However this makes a Service background, so system will kill it in short period of time.

How Spotify and Google Music could workaround it? What is the way to achieve it?

Tim
  • 41,901
  • 18
  • 127
  • 145
  • 1
    that is not possible. But I think you already knew that – Tim Nov 27 '18 at 14:49
  • @TimCastelijns Spotify and Google Music does it in some way, so I suppose it is possible. I don't say they do it in a way I described, using stop/start foreground methods. – Marcin Adamczewski Nov 28 '18 at 09:52
  • 1
    possible for them doesn't mean possible for the rest of us. Spotify is a very big party, they might have some deal with google. And google/music themselves can do whatever they want. – Tim Nov 28 '18 at 09:57
  • @TimCastelijns I rephrased my question to not be considered as duplicate anymore. – Marcin Adamczewski Nov 29 '18 at 09:07
  • my previous comment stands. These guys don't play by the same rules as us – Tim Nov 29 '18 at 09:15
  • It's easy to identify from the AndroidManifest.xml if another application works in a different way android provides the system app flag for that cases! Google music should be a system app! Because it is also pre-installed and cannot completely removed. But spotify is not (at least in Android one devices). I've been struggling with a similar problem when trying to run a "persistent" service for a clock like widget and start believing that the system has some extra permissions! But check the source code it doesn't. You just have to dig further and get out of the logic that you already set it. – madlymad Apr 02 '19 at 05:24

5 Answers5

3

Using a combination of Pouya's answer and some independent research, I have a code snippet that behaves fairly close to apps like Spotify using the OOTB MediaPlayer. There is probably a more elegant solution, but this is the quick and dirty version - I have too much code scattered about to post, so have an overview instead:

When the mediaplayer is paused, I run two pieces of code. First, after updating the notification bar I demote it from a foreground service to a background one with:

public void demoteNotifyBar() {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
            stopForeground(STOP_FOREGROUND_DETACH);
        else
            stopForeground(false);
    }

and then I start a new timer, that remakes the notification bar once a second:

// Put this in the onCreate method
pauseRefresh = new Runnable() {
            @Override
            public void run() {
                // Check if media is playing to prevent race conditions
                if (!isPlaying()) {
                    startForeground(FOREGROUND_SERVICE, notification);
                    demoteNotifyBar();
                    handler.postDelayed(pauseRefresh, 1000);
                }
            }
        };

// Call this wherever you're handling the pause button
pauseRefresh.run();

To cancel the auto-refresh, you'll need to call this bit of code before you remake the notification bar and toggle the media from pause to playing (yes, you can ignore this for toggling audio, but read below for why this is needed):

handler.removeCallbacks(pauseRefresh);

You will also need to listen for the swipe dismiss with a delete intent, to prevent the notification from remaking itself when the user dismisses it; simply use the same code to cancel the timer that is used when the user clicks play.

Christian Boler
  • 151
  • 2
  • 17
  • Question: Why did you pick 1 second intervals? Did you try longer intervals? That just seems like it's gonna waste lots of battery. Does your app get flagged as a battery waster by the OS? I know the OS will kill a background service fairly quickly, but not that quickly. Is it? – Glaucus Apr 13 '20 at 19:49
2

I think you can have a timer in your service that restarts your service after a while if the user stopped the music your timer will keep it from dying and if the user clicked play button do as what you already do before.

Pouya Danesh
  • 1,557
  • 2
  • 19
  • 36
1

Did you tried to use

NotificationCompat.Builder()
...
.setOngoing(true)
...
.build()

?

Should do the trick.

Actually, you don't need to stop your foreground. Just use this setting. When you want to change this - just build new Notification with ongoing == false and then call it like:

notificationManager.notify( id, yourNotification )
Victor Cold
  • 603
  • 5
  • 15
  • This should be the accepted answer. I use it in my audio player. I see other audio apps also do it, f.e: https://github.com/kabouzeid/Phonograph/blob/60f5c7221902acd70175f13ceb33f7de7d878630/app/src/main/java/com/kabouzeid/gramophone/service/notification/PlayingNotificationImpl24.java#L89 – ufoq Sep 23 '20 at 17:07
0

so you want when another application is playing their music you want your notification dismissable Right. you need to do is when you create notification (I guess in Mediaprepare() or click or something else), when you create the notification you set builder.setOngoing(true), and when you user pauses music or other application use media service you set builder.setOngoing(false) so user can easily dismiss your notification.

Happy Coding

Jigar Fumakiya
  • 2,039
  • 1
  • 8
  • 21
0

I have tried using flag STOP_FOREGROUND_DETACH for stopForeground() and it seems to work, allowing the notification to be swiped while not clearing the notification by OS after a while.

Mayb3Not
  • 441
  • 4
  • 10