ryanheise / audio_service

Flutter plugin to play audio in the background while the screen is off.
805 stars 480 forks source link

Stopping the audio doesnt remove the notification #1082

Open animesh-flashfeed opened 4 months ago

animesh-flashfeed commented 4 months ago

Documented behaviour

stop() → Future Stop playback and release resources.

I have used both for dismissing the notification
await audioHandler?.stop(); await BaseAudioHandler().stop();

Actual behaviour

The notification doesn't get dismissed

Minimal reproduction project

Official example: example_playlist.dart

Reproduction steps

  1. Click the stop button and it doesn't dismiss the notification

    Output of flutter doctor

    
    [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 15.4)
    [✓] Chrome - develop for the web
    [✓] Android Studio (version 2024.1)
    [✓] VS Code (version 1.91.1)
    [✓] Connected device (4 available)            
    [✓] Network resources

• No issues found!```

Devices exhibiting the bug

Pixel 6a, Android Version 14

ryanheise commented 4 months ago

I have used both for dismissing the notification await audioHandler?.stop(); await BaseAudioHandler().stop();

Your code is not pertinent to the bug report because your bug report submits the "official example: example_playlist.dart" as the minimal reproduction project.

Note that there are several examples demonstrating different features of audio_service. If you want to see an example that demonstrates how to close the notification, see lib/main.dart.

XuanTung4195 commented 4 months ago

Same and I've done some debugging. It does not cancel the notification even though it did call NotificationManager.cancel();
Check the answer here The legacyStopForeground() must be called before onDestroy() or else the notification cannot be removed by code.

IliaKhuzhakhmetov commented 4 months ago

Got the same issue

KRTirtho commented 3 months ago

Facing the same issue here. But, this only happens when androidStopForegroundOnPause: false and androidNotificationOngoing: false if both are true the service does stop as expected after removing the app from task manager

But, this results in an issue where user pauses for momentarily and the OS kills the whole app for memory which is not ideal. This is espeically bad in Chinese android skins. Those OEM skins are too much aggressive

One cheeky way to get around this is while keeping both androidNotificationOngoing: false, androidStopForegroundOnPause: false, is to exit(0) on the BaseAudioHandler's overriden onTaskRemoved method

class MyAudioService extends BaseAudioHandler {
  // ... other stuff
  @override
  Future<void> onTaskRemoved() async {
    await myAudioPlayer.stop();
    if(Platform.isAndroid) exit(0);
  }
 // ... other stuff
}

[!CAUTION] This can get the app rejected on AppStore. Usually, programmatic process termination is somehow against their policy.

ryanheise commented 3 months ago

This behaviour can be controlled through controlled state transitions although I will also investigate @XuanTung4195 's note above as this may help it to work more robustly.

nateshmbhat commented 2 months ago

facing the same issue. the behavior is inconsistent. sometimes the notification gets removed but sometimes it just stays.

fuz12 commented 1 month ago

I have the same problem. I wrote my own audio service on media3. The notification deletion works. It's all about the removeSession method, maybe it's worth going this way too. If necessary, I can send a code with how to use my service.

class PlaybackService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    // Create your Player and MediaSession in the onCreate lifecycle event
    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        Log.d("PlaybackService", "onTaskRemoved")
        var condition = mediaSession?.player?.playWhenReady;
        if (mediaSession != null) {
            removeSession(mediaSession!!)
        }
        if (condition == null || !condition) {
            stopSelf()
        }
    }

    override fun onUpdateNotification(session: MediaSession, startInForegroundRequired: Boolean) {
        Log.d("PlaybackService", "onUpdateNotification")

        super.onUpdateNotification(session, startInForegroundRequired)
    }

    // Remember to release the player and media session in onDestroy
    @OptIn(UnstableApi::class)
    override fun onDestroy() {
        mediaSession?.run {
            Log.d("PlaybackService", "onDestroy")
            player.release()
            stopForeground(STOP_FOREGROUND_REMOVE)
            pauseAllPlayersAndStopSelf()
            if (mediaSession != null) removeSession(mediaSession!!)
            release()
            mediaSession = null
        }
        super.onDestroy()
    }

    // This example always accepts the connection request
    override fun onGetSession(
        controllerInfo: MediaSession.ControllerInfo
    ): MediaSession? = mediaSession
}