audio_service icon indicating copy to clipboard operation
audio_service copied to clipboard

iOS audio control center still present after ProcessingState.completed and even super.stop()

Open DGempler opened this issue 3 years ago • 11 comments

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:

  1. git checkout a55f565 to go back to master version, without me adding the super.stop() call
  2. Run the example_playlist demo
  3. Click on last track
  4. Click Play
  5. Move slider to very right of track (there are a few seconds of play left even after the "end" of the track)
  6. Access control center and observe what happens on ProcessingState.completed and subsequent stop()
  7. git checkout a7ff302 to go back to my first commit, repeat #2-6 to see that nothing changes
  8. 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: 4E2E7CE4-A053-4089-ABA9-72F668E57896 After hack fix commits: CD44BF4B-9833-4886-978B-D40540D67713

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 updateControlsmethod 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?

DGempler avatar Sep 23 '21 17:09 DGempler

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?

ryanheise avatar Sep 24 '21 02:09 ryanheise

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!

DGempler avatar Sep 24 '21 04:09 DGempler

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

nt4f04uNd avatar Sep 24 '21 10:09 nt4f04uNd

@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?

DGempler avatar Sep 24 '21 14:09 DGempler

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.

ryanheise avatar Sep 24 '21 15:09 ryanheise

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?

DGempler avatar Sep 24 '21 22:09 DGempler

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?

ryanheise avatar Oct 03 '21 09:10 ryanheise

Just to check up on this, have you had a chance to test the latest commit on your app?

ryanheise avatar Oct 10 '21 07:10 ryanheise

Hey @ryanheise sorry, missed your comments. Should be able to test this later this week and will let you know. Thanks!

DGempler avatar Oct 12 '21 21:10 DGempler

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!

DGempler avatar Oct 14 '21 23:10 DGempler

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.

ryanheise avatar Oct 15 '21 01:10 ryanheise