-
Notifications
You must be signed in to change notification settings - Fork 473
Android service update #2897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Android service update #2897
Changes from all commits
b631602
ea992b3
3dfb124
dc0cc91
c0c7eb8
efdde5b
336d5ab
33da943
a712311
d1f2fc3
83cac6c
d33a686
e4fcf8e
b95adbb
e779753
1acf0f0
d7dd2ef
5dff776
e33100a
fd4e473
7971fd3
7e286ac
3603e79
bc3d944
55b6062
edfed8e
ca87c60
7ca828b
8a27ddc
8d4139f
4a2dd06
9155b08
e777736
cbb31ad
cfc48ec
b7084c7
2a96736
3ac2469
5e2aac8
badbcc8
373086b
02c7e49
7fd4161
8c5677b
2131c78
93e22f8
29d1290
df44b04
0689336
aead30e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4,8 +4,8 @@ | |||||
| xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
| android:id="@+id/texture_view_media_element" | ||||||
| app:surface_type="texture_view" | ||||||
| app:shutter_background_color="#FFFFFF" | ||||||
| app:shutter_background_color="#000000" | ||||||
|
||||||
| app:shutter_background_color="#000000" | |
| app:shutter_background_color="#FFFFFF" |
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,180 +1,94 @@ | ||||||||||||||||
| using System.Diagnostics.CodeAnalysis; | ||||||||||||||||
| using System.Runtime.Versioning; | ||||||||||||||||
| using System.Runtime.Versioning; | ||||||||||||||||
| using Android.App; | ||||||||||||||||
| using Android.Content; | ||||||||||||||||
| using Android.Content.PM; | ||||||||||||||||
| using Android.OS; | ||||||||||||||||
| using AndroidX.Core.App; | ||||||||||||||||
| using AndroidX.Media3.Common; | ||||||||||||||||
| using AndroidX.Media3.DataSource; | ||||||||||||||||
| using AndroidX.Media3.ExoPlayer; | ||||||||||||||||
| using AndroidX.Media3.ExoPlayer.TrackSelection; | ||||||||||||||||
| using AndroidX.Media3.Session; | ||||||||||||||||
| using AndroidX.Media3.UI; | ||||||||||||||||
| using CommunityToolkit.Maui.Services; | ||||||||||||||||
| using Resource = Microsoft.Maui.Controls.Resource; | ||||||||||||||||
| using Java.Util; | ||||||||||||||||
|
|
||||||||||||||||
| namespace CommunityToolkit.Maui.Media.Services; | ||||||||||||||||
|
|
||||||||||||||||
| [SupportedOSPlatform("Android26.0")] | ||||||||||||||||
| [IntentFilter(["androidx.media3.session.MediaSessionService"])] | ||||||||||||||||
| [Service(Exported = false, Enabled = true, Name = "communityToolkit.maui.media.services", ForegroundServiceType = ForegroundService.TypeMediaPlayback)] | ||||||||||||||||
| sealed partial class MediaControlsService : Service | ||||||||||||||||
| sealed partial class MediaControlsService : MediaSessionService | ||||||||||||||||
| { | ||||||||||||||||
| readonly WeakEventManager taskRemovedEventManager = new(); | ||||||||||||||||
|
|
||||||||||||||||
| bool isDisposed; | ||||||||||||||||
|
|
||||||||||||||||
| PlayerNotificationManager? playerNotificationManager; | ||||||||||||||||
| NotificationCompat.Builder? notificationBuilder; | ||||||||||||||||
|
|
||||||||||||||||
| public event EventHandler TaskRemoved | ||||||||||||||||
| { | ||||||||||||||||
| add => taskRemovedEventManager.AddEventHandler(value); | ||||||||||||||||
| remove => taskRemovedEventManager.RemoveEventHandler(value); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| public BoundServiceBinder? Binder { get; private set; } | ||||||||||||||||
| public NotificationManager? NotificationManager { get; private set; } | ||||||||||||||||
|
|
||||||||||||||||
| public override IBinder? OnBind(Intent? intent) | ||||||||||||||||
| { | ||||||||||||||||
| Binder = new BoundServiceBinder(this); | ||||||||||||||||
| return Binder; | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| public override void OnCreate() | ||||||||||||||||
| { | ||||||||||||||||
| base.OnCreate(); | ||||||||||||||||
| StartForegroundServices(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| public override StartCommandResult OnStartCommand(Intent? intent, StartCommandFlags flags, int startId) | ||||||||||||||||
| => StartCommandResult.NotSticky; | ||||||||||||||||
| MediaSession? mediaSession; | ||||||||||||||||
| IExoPlayer? exoPlayer; | ||||||||||||||||
| DefaultTrackSelector? trackSelector; | ||||||||||||||||
|
|
||||||||||||||||
| public override void OnTaskRemoved(Intent? rootIntent) | ||||||||||||||||
| { | ||||||||||||||||
| base.OnTaskRemoved(rootIntent); | ||||||||||||||||
| taskRemovedEventManager.HandleEvent(this, EventArgs.Empty, nameof(TaskRemoved)); | ||||||||||||||||
|
|
||||||||||||||||
| playerNotificationManager?.SetPlayer(null); | ||||||||||||||||
| NotificationManager?.CancelAll(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| public override void OnDestroy() | ||||||||||||||||
| { | ||||||||||||||||
| base.OnDestroy(); | ||||||||||||||||
|
|
||||||||||||||||
| playerNotificationManager?.SetPlayer(null); | ||||||||||||||||
| NotificationManager?.CancelAll(); | ||||||||||||||||
| if (!OperatingSystem.IsAndroidVersionAtLeast(33)) | ||||||||||||||||
| { | ||||||||||||||||
| StopForeground(true); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| StopSelf(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| public override void OnRebind(Intent? intent) | ||||||||||||||||
| { | ||||||||||||||||
| base.OnRebind(intent); | ||||||||||||||||
| StartForegroundServices(); | ||||||||||||||||
| PauseAllPlayersAndStopSelf(); | ||||||||||||||||
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
ne0rrmatrix marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| [MemberNotNull(nameof(NotificationManager), nameof(notificationBuilder))] | ||||||||||||||||
| public void UpdateNotifications(in MediaSession session, in PlatformMediaElement mediaElement) | ||||||||||||||||
| { | ||||||||||||||||
| ArgumentNullException.ThrowIfNull(notificationBuilder); | ||||||||||||||||
| ArgumentNullException.ThrowIfNull(NotificationManager); | ||||||||||||||||
|
|
||||||||||||||||
| var style = new MediaStyleNotificationHelper.MediaStyle(session); | ||||||||||||||||
| if (!OperatingSystem.IsAndroidVersionAtLeast(33)) | ||||||||||||||||
| { | ||||||||||||||||
| SetLegacyNotifications(session, mediaElement); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| notificationBuilder.SetStyle(style); | ||||||||||||||||
| NotificationManagerCompat.From(Platform.AppContext)?.Notify(1, notificationBuilder.Build()); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| [MemberNotNull(nameof(playerNotificationManager))] | ||||||||||||||||
| public void SetLegacyNotifications(in MediaSession session, in PlatformMediaElement mediaElement) | ||||||||||||||||
| public override void OnCreate() | ||||||||||||||||
| { | ||||||||||||||||
| ArgumentNullException.ThrowIfNull(session); | ||||||||||||||||
| playerNotificationManager ??= new PlayerNotificationManager.Builder(Platform.AppContext, 1, "1").Build() | ||||||||||||||||
| ?? throw new InvalidOperationException("PlayerNotificationManager cannot be null"); | ||||||||||||||||
| base.OnCreate(); | ||||||||||||||||
|
|
||||||||||||||||
| playerNotificationManager.SetUseFastForwardAction(true); | ||||||||||||||||
| playerNotificationManager.SetUseFastForwardActionInCompactView(true); | ||||||||||||||||
| playerNotificationManager.SetUseRewindAction(true); | ||||||||||||||||
| playerNotificationManager.SetUseRewindActionInCompactView(true); | ||||||||||||||||
| playerNotificationManager.SetUseNextAction(true); | ||||||||||||||||
| playerNotificationManager.SetUseNextActionInCompactView(true); | ||||||||||||||||
| playerNotificationManager.SetUsePlayPauseActions(true); | ||||||||||||||||
| playerNotificationManager.SetUsePreviousAction(true); | ||||||||||||||||
| playerNotificationManager.SetColor(Resource.Color.abc_primary_text_material_dark); | ||||||||||||||||
| playerNotificationManager.SetUsePreviousActionInCompactView(true); | ||||||||||||||||
| playerNotificationManager.SetVisibility(NotificationCompat.VisibilityPublic); | ||||||||||||||||
| playerNotificationManager.SetMediaSessionToken(session.PlatformToken); | ||||||||||||||||
| playerNotificationManager.SetPlayer(mediaElement); | ||||||||||||||||
| playerNotificationManager.SetColorized(true); | ||||||||||||||||
| playerNotificationManager.SetShowPlayButtonIfPlaybackIsSuppressed(true); | ||||||||||||||||
| playerNotificationManager.SetSmallIcon(Resource.Drawable.media3_notification_small_icon); | ||||||||||||||||
| playerNotificationManager.SetPriority(NotificationCompat.PriorityDefault); | ||||||||||||||||
| playerNotificationManager.SetUseChronometer(true); | ||||||||||||||||
| var audioAttribute = new AndroidX.Media3.Common.AudioAttributes.Builder()? | ||||||||||||||||
| .SetContentType(C.AudioContentTypeMusic)? // When phonecalls come in, music is paused | ||||||||||||||||
| .SetUsage(C.UsageMedia)? | ||||||||||||||||
| .Build(); | ||||||||||||||||
|
|
||||||||||||||||
| trackSelector = new DefaultTrackSelector(this); | ||||||||||||||||
| var trackSelectionParameters = trackSelector.BuildUponParameters()? | ||||||||||||||||
| .SetPreferredAudioLanguage(C.LanguageUndetermined)? // Fallback to system language if no preferred language found | ||||||||||||||||
| .SetPreferredTextLanguage(C.LanguageUndetermined)? // Fallback to system language if no preferred language found | ||||||||||||||||
| .SetIgnoredTextSelectionFlags(C.SelectionFlagAutoselect); // Ignore text tracks that are not explicitly selected by the user | ||||||||||||||||
| trackSelector.SetParameters((DefaultTrackSelector.Parameters.Builder?)trackSelectionParameters); // Allows us to select tracks based on user preferences | ||||||||||||||||
|
|
||||||||||||||||
| var loadControlBuilder = new DefaultLoadControl.Builder(); | ||||||||||||||||
| loadControlBuilder.SetBufferDurationsMs( | ||||||||||||||||
| minBufferMs: 15000, | ||||||||||||||||
| maxBufferMs: 50000, | ||||||||||||||||
| bufferForPlaybackMs: 2500, | ||||||||||||||||
| bufferForPlaybackAfterRebufferMs: 5000); // Custom buffering strategy | ||||||||||||||||
|
|
||||||||||||||||
| var builder = new ExoPlayerBuilder(this) ?? throw new InvalidOperationException("ExoPlayerBuilder returned null"); | ||||||||||||||||
| builder.SetTrackSelector(trackSelector); | ||||||||||||||||
| builder.SetAudioAttributes(audioAttribute, true); | ||||||||||||||||
| builder.SetHandleAudioBecomingNoisy(true); // Unplugging headphones will pause playback | ||||||||||||||||
| builder.SetLoadControl(loadControlBuilder.Build()); | ||||||||||||||||
| exoPlayer = builder.Build() ?? throw new InvalidOperationException("ExoPlayerBuilder.Build() returned null"); | ||||||||||||||||
|
|
||||||||||||||||
| var mediaSessionBuilder = new MediaSession.Builder(this, exoPlayer); | ||||||||||||||||
| UUID sessionId = UUID.RandomUUID() ?? throw new InvalidOperationException("UUID.RandomUUID() returned null"); | ||||||||||||||||
| mediaSessionBuilder.SetId(sessionId.ToString()); | ||||||||||||||||
|
|
||||||||||||||||
| var dataSourceBitmapFactory = new DataSourceBitmapLoader(this); | ||||||||||||||||
| mediaSessionBuilder.SetBitmapLoader(dataSourceBitmapFactory); | ||||||||||||||||
| mediaSession = mediaSessionBuilder.Build() ?? throw new InvalidOperationException("MediaSession.Builder.Build() returned null"); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| protected override void Dispose(bool disposing) | ||||||||||||||||
| { | ||||||||||||||||
| if (!isDisposed) | ||||||||||||||||
| if (disposing) | ||||||||||||||||
| { | ||||||||||||||||
| if (disposing) | ||||||||||||||||
| { | ||||||||||||||||
| NotificationManager?.Dispose(); | ||||||||||||||||
| NotificationManager = null; | ||||||||||||||||
|
|
||||||||||||||||
| playerNotificationManager?.Dispose(); | ||||||||||||||||
| playerNotificationManager = null; | ||||||||||||||||
|
|
||||||||||||||||
| if (!OperatingSystem.IsAndroidVersionAtLeast(33)) | ||||||||||||||||
| { | ||||||||||||||||
| StopForeground(true); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| StopSelf(); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| isDisposed = true; | ||||||||||||||||
| PauseAllPlayersAndStopSelf(); | ||||||||||||||||
| mediaSession?.Release(); | ||||||||||||||||
| mediaSession?.Dispose(); | ||||||||||||||||
| mediaSession = null; | ||||||||||||||||
| exoPlayer?.Release(); | ||||||||||||||||
|
Comment on lines
+75
to
+76
|
||||||||||||||||
| mediaSession = null; | |
| exoPlayer?.Release(); | |
| mediaSession = null; | |
| exoPlayer?.Stop(); | |
| exoPlayer?.ClearMediaItems(); | |
| exoPlayer?.Release(); | |
| exoPlayer?.Dispose(); |
Uh oh!
There was an error while loading. Please reload this page.