@@ -16,149 +16,162 @@ namespace CommunityToolkit.Maui.Media.Services;
16
16
[ Service ( Exported = false , Enabled = true , Name = "communityToolkit.maui.media.services" , ForegroundServiceType = ForegroundService . TypeMediaPlayback ) ]
17
17
sealed partial class MediaControlsService : Service
18
18
{
19
- bool isDisposed ;
20
-
21
- public MediaSession ? Session ;
22
- public PlatformMediaElement ? Player ;
23
-
24
- public NotificationManager ? NotificationManager ;
25
- PlayerNotificationManager ? playerNotificationManager ;
26
- NotificationCompat . Builder ? notification ;
27
-
28
- public PlayerView ? PlayerView { get ; set ; }
29
-
30
- public BoundServiceBinder ? Binder = null ;
31
-
32
- public override IBinder ? OnBind ( Intent ? intent )
33
- {
34
- Binder = new BoundServiceBinder ( this ) ;
35
- return Binder ;
36
- }
37
- public override void OnCreate ( )
38
- {
39
- base . OnCreate ( ) ;
40
- StartForegroundServices ( ) ;
41
- }
42
- public override StartCommandResult OnStartCommand ( Intent ? intent , StartCommandFlags flags , int startId )
43
- {
44
- return StartCommandResult . NotSticky ;
45
- }
46
-
47
- public override void OnTaskRemoved ( Intent ? rootIntent )
48
- {
49
- base . OnTaskRemoved ( rootIntent ) ;
50
- Player ? . Stop ( ) ;
51
- playerNotificationManager ? . SetPlayer ( null ) ;
52
- NotificationManager ? . CancelAll ( ) ;
53
- }
54
-
55
- public override void OnDestroy ( )
56
- {
57
- base . OnDestroy ( ) ;
58
- playerNotificationManager ? . SetPlayer ( null ) ;
59
- NotificationManager ? . CancelAll ( ) ;
60
- if ( OperatingSystem . IsAndroidVersionAtLeast ( 26 ) && ! OperatingSystem . IsAndroidVersionAtLeast ( 33 ) )
61
- {
62
- StopForeground ( true ) ;
63
- }
64
- StopSelf ( ) ;
65
- }
66
- protected override void Dispose ( bool disposing )
67
- {
68
- if ( ! isDisposed )
69
- {
70
- if ( disposing )
71
- {
72
- NotificationManager ? . Dispose ( ) ;
73
- playerNotificationManager ? . Dispose ( ) ;
74
- if ( OperatingSystem . IsAndroidVersionAtLeast ( 26 ) && ! OperatingSystem . IsAndroidVersionAtLeast ( 33 ) )
75
- {
76
- StopForeground ( true ) ;
77
- }
78
- StopSelf ( ) ;
79
- }
80
- isDisposed = true ;
81
- }
82
- base . Dispose ( disposing ) ;
83
- }
84
-
85
- public override void OnRebind ( Intent ? intent )
86
- {
87
- base . OnRebind ( intent ) ;
88
- StartForegroundServices ( ) ;
89
- }
90
-
91
- [ MemberNotNull ( nameof ( NotificationManager ) ) ]
92
- static void CreateNotificationChannel ( NotificationManager notificationMnaManager )
93
- {
94
- if ( OperatingSystem . IsAndroidVersionAtLeast ( 26 ) )
95
- {
96
- var channel = new NotificationChannel ( "1" , "1" , NotificationImportance . Low ) ;
97
- notificationMnaManager . CreateNotificationChannel ( channel ) ;
98
- }
99
- }
100
-
101
- [ MemberNotNull ( nameof ( notification ) , nameof ( NotificationManager ) ) ]
102
- void StartForegroundServices ( )
103
- {
104
- NotificationManager ??= GetSystemService ( NotificationService ) as NotificationManager ?? throw new InvalidOperationException ( $ "{ nameof ( NotificationManager ) } cannot be null") ;
105
- notification ??= new NotificationCompat . Builder ( Platform . AppContext , "1" ) ;
106
-
107
- notification . SetSmallIcon ( Resource . Drawable . media3_notification_small_icon ) ;
108
- notification . SetAutoCancel ( false ) ;
109
- notification . SetForegroundServiceBehavior ( NotificationCompat . ForegroundServiceImmediate ) ;
110
- notification . SetVisibility ( NotificationCompat . VisibilityPublic ) ;
111
-
112
- CreateNotificationChannel ( NotificationManager ) ;
113
-
114
- if ( OperatingSystem . IsAndroidVersionAtLeast ( 29 ) )
115
- {
116
- StartForeground ( 1 , notification . Build ( ) , ForegroundService . TypeMediaPlayback ) ;
117
- return ;
118
- }
119
-
120
- StartForeground ( 1 , notification . Build ( ) ) ;
121
- }
122
-
123
- [ MemberNotNull ( nameof ( NotificationManager ) , nameof ( notification ) ) ]
124
- public void UpdateNotifications ( )
125
- {
126
- ArgumentNullException . ThrowIfNull ( notification ) ;
127
- ArgumentNullException . ThrowIfNull ( NotificationManager ) ;
128
-
129
- var style = new MediaStyleNotificationHelper . MediaStyle ( Session ) ;
130
- if ( ! OperatingSystem . IsAndroidVersionAtLeast ( 33 ) )
131
- {
132
- SetLegacyNotifications ( ) ;
133
- }
134
- notification . SetStyle ( style ) ;
135
- NotificationManagerCompat . From ( Platform . AppContext ) . Notify ( 1 , notification . Build ( ) ) ;
136
- }
137
-
138
- [ MemberNotNull ( nameof ( playerNotificationManager ) ) ]
139
- public void SetLegacyNotifications ( )
140
- {
141
- playerNotificationManager ??= new PlayerNotificationManager . Builder ( Platform . AppContext , 1 , "1" ) . Build ( ) ;
142
- ArgumentNullException . ThrowIfNull ( Session ) ;
143
- ArgumentNullException . ThrowIfNull ( playerNotificationManager ) ;
144
-
145
- playerNotificationManager . SetUseFastForwardAction ( true ) ;
146
- playerNotificationManager . SetUseFastForwardActionInCompactView ( true ) ;
147
- playerNotificationManager . SetUseRewindAction ( true ) ;
148
- playerNotificationManager . SetUseRewindActionInCompactView ( true ) ;
149
- playerNotificationManager . SetUseNextAction ( true ) ;
150
- playerNotificationManager . SetUseNextActionInCompactView ( true ) ;
151
- playerNotificationManager . SetUsePlayPauseActions ( true ) ;
152
- playerNotificationManager . SetUsePreviousAction ( true ) ;
153
- playerNotificationManager . SetColor ( Resource . Color . abc_primary_text_material_dark ) ;
154
- playerNotificationManager . SetUsePreviousActionInCompactView ( true ) ;
155
- playerNotificationManager . SetVisibility ( NotificationCompat . VisibilityPublic ) ;
156
- playerNotificationManager . SetMediaSessionToken ( Session . SessionCompatToken ) ;
157
- playerNotificationManager . SetPlayer ( Player ) ;
158
- playerNotificationManager . SetColorized ( true ) ;
159
- playerNotificationManager . SetShowPlayButtonIfPlaybackIsSuppressed ( true ) ;
160
- playerNotificationManager . SetSmallIcon ( Resource . Drawable . media3_notification_small_icon ) ;
161
- playerNotificationManager . SetPriority ( NotificationCompat . PriorityDefault ) ;
162
- playerNotificationManager . SetUseChronometer ( true ) ;
163
- }
19
+ readonly WeakEventManager taskRemovedEventManager = new ( ) ;
20
+
21
+ bool isDisposed ;
22
+
23
+ PlayerNotificationManager ? playerNotificationManager ;
24
+ NotificationCompat . Builder ? notification ;
25
+
26
+ public event EventHandler TaskRemoved
27
+ {
28
+ add => taskRemovedEventManager . AddEventHandler ( value ) ;
29
+ remove => taskRemovedEventManager . RemoveEventHandler ( value ) ;
30
+ }
31
+
32
+ public BoundServiceBinder ? Binder { get ; private set ; }
33
+ public NotificationManager ? NotificationManager { get ; private set ; }
34
+
35
+ public override IBinder ? OnBind ( Intent ? intent )
36
+ {
37
+ Binder = new BoundServiceBinder ( this ) ;
38
+ return Binder ;
39
+ }
40
+
41
+ public override void OnCreate ( )
42
+ {
43
+ base . OnCreate ( ) ;
44
+ StartForegroundServices ( ) ;
45
+ }
46
+
47
+ public override StartCommandResult OnStartCommand ( Intent ? intent , StartCommandFlags flags , int startId )
48
+ => StartCommandResult . NotSticky ;
49
+
50
+ public override void OnTaskRemoved ( Intent ? rootIntent )
51
+ {
52
+ base . OnTaskRemoved ( rootIntent ) ;
53
+ taskRemovedEventManager . HandleEvent ( this , EventArgs . Empty , nameof ( TaskRemoved ) ) ;
54
+
55
+ playerNotificationManager ? . SetPlayer ( null ) ;
56
+ NotificationManager ? . CancelAll ( ) ;
57
+ }
58
+
59
+ public override void OnDestroy ( )
60
+ {
61
+ base . OnDestroy ( ) ;
62
+
63
+ playerNotificationManager ? . SetPlayer ( null ) ;
64
+ NotificationManager ? . CancelAll ( ) ;
65
+ if ( ! OperatingSystem . IsAndroidVersionAtLeast ( 33 ) )
66
+ {
67
+ StopForeground ( true ) ;
68
+ }
69
+
70
+ StopSelf ( ) ;
71
+ }
72
+
73
+ protected override void Dispose ( bool disposing )
74
+ {
75
+ if ( ! isDisposed )
76
+ {
77
+ if ( disposing )
78
+ {
79
+ NotificationManager ? . Dispose ( ) ;
80
+ NotificationManager = null ;
81
+
82
+ playerNotificationManager ? . Dispose ( ) ;
83
+ playerNotificationManager = null ;
84
+
85
+ if ( ! OperatingSystem . IsAndroidVersionAtLeast ( 33 ) )
86
+ {
87
+ StopForeground ( true ) ;
88
+ }
89
+
90
+ StopSelf ( ) ;
91
+ }
92
+
93
+ isDisposed = true ;
94
+ }
95
+
96
+ base . Dispose ( disposing ) ;
97
+ }
98
+
99
+ public override void OnRebind ( Intent ? intent )
100
+ {
101
+ base . OnRebind ( intent ) ;
102
+ StartForegroundServices ( ) ;
103
+ }
104
+
105
+ [ MemberNotNull ( nameof ( NotificationManager ) , nameof ( notification ) ) ]
106
+ public void UpdateNotifications ( in MediaSession session , in PlatformMediaElement mediaElement )
107
+ {
108
+ ArgumentNullException . ThrowIfNull ( notification ) ;
109
+ ArgumentNullException . ThrowIfNull ( NotificationManager ) ;
110
+
111
+ var style = new MediaStyleNotificationHelper . MediaStyle ( session ) ;
112
+ if ( ! OperatingSystem . IsAndroidVersionAtLeast ( 33 ) )
113
+ {
114
+ SetLegacyNotifications ( session , mediaElement ) ;
115
+ }
116
+
117
+ notification . SetStyle ( style ) ;
118
+ NotificationManagerCompat . From ( Platform . AppContext ) . Notify ( 1 , notification . Build ( ) ) ;
119
+ }
120
+
121
+ [ MemberNotNull ( nameof ( playerNotificationManager ) ) ]
122
+ public void SetLegacyNotifications ( in MediaSession session , in PlatformMediaElement mediaElement )
123
+ {
124
+ ArgumentNullException . ThrowIfNull ( session ) ;
125
+ playerNotificationManager ??= new PlayerNotificationManager . Builder ( Platform . AppContext , 1 , "1" ) . Build ( )
126
+ ?? throw new InvalidOperationException ( "PlayerNotificationManager cannot be null" ) ;
127
+
128
+ playerNotificationManager . SetUseFastForwardAction ( true ) ;
129
+ playerNotificationManager . SetUseFastForwardActionInCompactView ( true ) ;
130
+ playerNotificationManager . SetUseRewindAction ( true ) ;
131
+ playerNotificationManager . SetUseRewindActionInCompactView ( true ) ;
132
+ playerNotificationManager . SetUseNextAction ( true ) ;
133
+ playerNotificationManager . SetUseNextActionInCompactView ( true ) ;
134
+ playerNotificationManager . SetUsePlayPauseActions ( true ) ;
135
+ playerNotificationManager . SetUsePreviousAction ( true ) ;
136
+ playerNotificationManager . SetColor ( Resource . Color . abc_primary_text_material_dark ) ;
137
+ playerNotificationManager . SetUsePreviousActionInCompactView ( true ) ;
138
+ playerNotificationManager . SetVisibility ( NotificationCompat . VisibilityPublic ) ;
139
+ playerNotificationManager . SetMediaSessionToken ( session . SessionCompatToken ) ;
140
+ playerNotificationManager . SetPlayer ( mediaElement ) ;
141
+ playerNotificationManager . SetColorized ( true ) ;
142
+ playerNotificationManager . SetShowPlayButtonIfPlaybackIsSuppressed ( true ) ;
143
+ playerNotificationManager . SetSmallIcon ( Resource . Drawable . media3_notification_small_icon ) ;
144
+ playerNotificationManager . SetPriority ( NotificationCompat . PriorityDefault ) ;
145
+ playerNotificationManager . SetUseChronometer ( true ) ;
146
+ }
147
+
148
+
149
+ static void CreateNotificationChannel ( in NotificationManager notificationMnaManager )
150
+ {
151
+ var channel = new NotificationChannel ( "1" , "1" , NotificationImportance . Low ) ;
152
+ notificationMnaManager . CreateNotificationChannel ( channel ) ;
153
+ }
154
+
155
+ [ MemberNotNull ( nameof ( notification ) , nameof ( NotificationManager ) ) ]
156
+ void StartForegroundServices ( )
157
+ {
158
+ NotificationManager ??= GetSystemService ( NotificationService ) as NotificationManager ?? throw new InvalidOperationException ( $ "{ nameof ( NotificationManager ) } cannot be null") ;
159
+ notification ??= new NotificationCompat . Builder ( Platform . AppContext , "1" ) ;
160
+
161
+ notification . SetSmallIcon ( Resource . Drawable . media3_notification_small_icon ) ;
162
+ notification . SetAutoCancel ( false ) ;
163
+ notification . SetForegroundServiceBehavior ( NotificationCompat . ForegroundServiceImmediate ) ;
164
+ notification . SetVisibility ( NotificationCompat . VisibilityPublic ) ;
165
+
166
+ CreateNotificationChannel ( NotificationManager ) ;
167
+
168
+ if ( OperatingSystem . IsAndroidVersionAtLeast ( 29 ) )
169
+ {
170
+ StartForeground ( 1 , notification . Build ( ) , ForegroundService . TypeMediaPlayback ) ;
171
+ }
172
+ else
173
+ {
174
+ StartForeground ( 1 , notification . Build ( ) ) ;
175
+ }
176
+ }
164
177
}
0 commit comments