XamarinMediaManager
XamarinMediaManager copied to clipboard
Notification stays even after swiping app away with app switcher
Device
Samsung S10+ running Android 11
🐛 Bug Report
Thanks for this great work.
The notification won't go away when CrossMediaManager.Current.Stop();
is called. The song stops, but the notification stays with play icon as shown on screenshot below. Even after the app is swiped away with android task switcher, the notification stays. The only way to make the notification go away is as follows.
- Swipe out the notification
- by force stopping (killing) the app from installed apps => app => settings in android
- by restarting the device.
Ironically, clicking the play icon on the orphaned notification restarts the song again from the first song of the playlist and it works as if the app was relaunched. Not sure if this is a new normal behaviors for Android 11. I started to have this issue since Android update.
I also tried to remove the notification manually by using NotificationManager.ClearAll()
but don't have any effect.
I noted that the media browser service never stops after Stop()
call as asserted in this line. So, the MediaBrowserService implementation never gets to hit its OnDestroy
block in this case. As a result the foreground notification stays along with the foreground media browser service.
I tried to debug the issue spending most of the time today, but its beyond me.
Expected behavior
The notification should disappear as soon as Stop()
is called or when app is swiped out.
Reproduction steps
Please find sample repository here to reproduce this issue. The repository is just a basic single page Xamarin.Forms app created with visual studio 2019.
Relevant code added to the default blank app is in below two lines, result is shown in screenshot above.
https://github.com/justcoding121/xamarin-sandbox/blob/main/Xamarin.SandboxApp/Xamarin.SandboxApp/Xamarin.SandboxApp.Android/MainActivity.cs#L26
https://github.com/justcoding121/xamarin-sandbox/blob/main/Xamarin.SandboxApp/Xamarin.SandboxApp/Xamarin.SandboxApp/MainPage.xaml.cs#L19
Configuration
Version: 1.0.8
Platform:
- [ ] :iphone: iOS
- [X] :robot: Android
- [ ] :checkered_flag: WPF
- [ ] :earth_americas: UWP
- [ ] :apple: MacOS
- [ ] :tv: tvOS
- [ ] :monkey: Xamarin.Forms
I figured this out. But can't post the solution here, since my code is intermingled with this library code.
The reason for this issue is API changes in version 30. This is explained in a comment on ExoPlayer project. https://github.com/google/ExoPlayer/issues/7964#issuecomment-702909316
- OnCancelled is not called anymore ins API 30 on Media Browser Service. The notification is expected to stay, and user will have the choice to swipe it out or resume playing. Swipe out will not trigger OnCancelled.
- The call to StopSelf() no longer immediately shuts down the service. The notification stays along with the service. The android system will decide when it should reclaim memory for service and execute its OnDestroy.
This created couple of scenarios that I had to deal with. Not sure if these scenarios are valid if everything is done correctly. I explain below what I did to solve it.
-
User calls
MediaManager.Stop()
from the app. Notification sticks around. Later, user decides to click the notification play icon, while another app is in Foreground. This triggers OnPrepare call. At this point, the MediaManager state is still valid. The MediaManager queue will have the playlist. But the position and the item played is reset. To resume playing, I manually set the position inside OnPrepare call, by pulling the last track and position from my app database. -
Service is shut down at some point by android system. But notification somehow sticks around (which it should'nt? But it happens to me). User clicks the play icon on notification. This recreates the service (OnCreate is hit). Next, OnPrepare is called. But now the playlist is gone. Queue is empty. So I pulled the last playlist from my app database, and started over again with creating the playlist and adjusting track and position to last played from inside OnPrepare call and return.
API 29 and below will not do any of above scenarios, so this logic is only for API 30 and above
That's amazing, I would like to implement your solution but am not sure of the right way to do it. Are you editing the source code and then changing the DLL within your project?
Thanks
Yes. You can take the code from this repository and include in your project with few changes.
Everything inside Platform folder will go to individual platform (Android, iOS, UWP) startup project. Remaining code can be put inside a shared .net standard 2.0/2.1 class library project. You will need to make few changes in the CrossMediaManager class , so that the correct MediaManagerImplementation is provided based on the platform. Make sure you have all the platform specific Nuget dependencies in your csproj files.
If you follow the code in official google media player example app (UAMP), specifically its media browser service implementation, you should be good for Android 11 (you may ignore cast feature, which I use, but you probably don't need). Android 11 has Playback resume feature as documented in developer website.
I see this behavior on a device with API 29, targeted the app to API 29 too. I reference fairly fresh AndroidX libraries in my Android Native app.
The notification does not work after it has been uninitialized, e.g. does not starts the playback. If it was playing, the pause button is visible, and it is not possible swipe out the notification.
The notification stays there unusable even if I have all of these in my uninitialization:
await CrossMediaManager.Current.Stop();
CrossMediaManager.Current.Notification.Enabled = false;
CrossMediaManager.Current.Dispose();
It doesn't matter if I call Dispose() or not.
Hi, I had the same problem, I suggest you do the following:
In your MainActivity when MediaManager starts, catch the MediaManager state change event to ensure that when the player stops it removes the notification.
CrossMediaManager.Current.Init(this);
CrossMediaManager.Current.StateChanged += (sender, e) =>
{
if (e.State == MediaManager.Player.MediaPlayerState.Stopped)
{
var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
notificationManager.CancelAll();
}
};
Hi, I had the same problem, I suggest you do the following:
In your MainActivity when MediaManager starts, catch the MediaManager state change event to ensure that when the player stops it removes the notification.
CrossMediaManager.Current.Init(this); CrossMediaManager.Current.StateChanged += (sender, e) => { if (e.State == MediaManager.Player.MediaPlayerState.Stopped) { var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager; notificationManager.CancelAll(); } };
Works nicely. I've called CancelAll() when I'm done with the player and want to dispose it. Many thanks!
@andresgutice @takacsalbert This workaround only works ones for me. If i open the app again notification stays. Any suggestions