audio_service
audio_service copied to clipboard
iOS audio control center still present after ProcessingState.completed and even super.stop()
Which API doesn't behave as documented, and how does it misbehave?
When player is stopped and ProcessingState
is completed
on Android, the player in the notification center is removed. However, on iOS, neither that completed
state nor calling super.stop() will remove the audio control center - it continues to be present and active. The expected behavior is for the control center's album art & audio controls to be disabled/removed, and for the behavior to be the same on iOS and Android.
Minimal reproduction project
Provide a link here using one of two options:
https://github.com/DGempler/audio_service/tree/dgempler/2021-09_iOS_control_center_persisting_demo
note: my first commit adds a call to super.stop()
to the stop()
override in the example_playlist.dart code in order to demonstrate the behavior. My next two commits are attempts at hacking together a quick fix 😊
To Reproduce (i.e. user steps, not code) Steps to reproduce the behavior:
-
git checkout a55f565
to go back to master version, without me adding thesuper.stop()
call - Run the example_playlist demo
- Click on last track
- Click Play
- Move slider to very right of track (there are a few seconds of play left even after the "end" of the track)
- Access control center and observe what happens on
ProcessingState.completed
and subsequentstop()
-
git checkout a7ff302
to go back to my first commit, repeat #2-6 to see that nothing changes -
git checkout edfdd46
and repeat #2-6 to see the expected behavior
Error messages
Expected behavior
The expected behavior is for the control center's album art & audio controls to be disabled/removed when either ProcessingState
is completed
or super.stop()
is called.
Screenshots
Before hack fix commits:
After hack fix commits:
Runtime Environment (please complete the following information if relevant):
- Device: iPhone SE 2nd generatior
- OS: iOS 14.8
Flutter SDK version
[✓] Flutter (Channel stable, 2.2.3, on macOS 11.6 20G165 darwin-x64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] Android Studio (version 3.4)
[✓] VS Code (version 1.60.1)
[✓] Connected device (2 available)
• No issues found!
Additional context First of all, thanks so much @ryanheise for all your work on this package, the app I've been developing on and off for a couple years now wouldn't be possible without it! Anyway, I remember having to create a custom solution for this very same iOS issue when working on this app over a year ago, in the old code. At the time I had deployed a successful version on Android but was never able to finish up the iOS side before getting too busy, but recently got back to the work (using the one isolate branch) and am very happy about the improvements made in the past year. Glad to see it's stable enough to be in master now.
So I believe that the updateControls
method isn't doing what it should be on stopService
. I enabled some of the logging when debugging and it seems that only a couple of the controls are affected on stopService
. I see there are some bitwise operations going on which may also impact it. I am not very familiar with Objective C syntax either, so don't know if I can help much with an actual fix, but happy to assist where I can and am able to test on a real device if needed.
Also, the controls only get disabled on stopService
, which only happens on super.stop()
for iOS and the behavior isn't the same on Android.
Also, I noticed that the loop in updateControls
stops at <= ASeekForward
and not ASetSpeed
- is that intentional?
Hi @DGempler thanks for your report and investigation. I suspect you are right on all counts. On iOS/macOS, ASetSpeed
was added later but I forgot to update the loop limit. For stopService
, the assumption is that the app should set its own state appropriately with the goal of disabling all actions. In your own debugging, did you notice any action that was not disabled?
Ahhh 🤦♂️ makes total sense, the app explicitly sets the controls so it should also "unset" them. I'll give it a shot tomorrow and will let you know. Thanks!
On iOS/macOS, ASetSpeed was added later but I forgot to update the loop limit.
Now that's why it didn't do anything lol
@ryanheise This branch is working as expected - please let me know if this looks correct:
https://github.com/DGempler/audio_service/blob/54aaefb051325535fbc39a56971a1fe8c59211ba/audio_service/example/lib/example_playlist.dart#L618-L633
@nt4f04uNd it worked with and without the ASetSpeed update for me - I'm guessing it may only be affected if that control is set?
It does look correct. Although I'm wondering whether it might be a good idea for stopService
on the iOS side to automatically disable all actions. That may create fewer surprises for the app developer.
On the other hand, I wonder whether there are any use cases I haven't thought of where the app developer may prefer to have complete control over this.
It makes sense to me that stopService
would disable and dispose of everything. But then again, I'm probably not the best person to answer since I was already expecting other behavior from the service 😄
Do you want me to create a PR for the enum fix?
Hi @DGempler
The latest commit changes stopService
to set actionBits = 0
which should end up causing all of the controls to be disabled and the notification to be removed. Would you like to test this?
Just to check up on this, have you had a chance to test the latest commit on your app?
Hey @ryanheise sorry, missed your comments. Should be able to test this later this week and will let you know. Thanks!
Hi @ryanheise
It works - I reverted my code (which manually updated the controls
and systemActions
) and it removed the control center controls on calling super.stop()
- when debugging on a physical iOS device anyway - the controls don't seem to go away on my emulator no matter what I try!
on a physical iOS device anyway - the controls don't seem to go away on my emulator no matter what I try!
I'll take that as a win :-) The emulator unfortunately doesn't seem to always do what it should.