5
5
import android .app .PendingIntent ;
6
6
import android .app .Service ;
7
7
import android .content .Intent ;
8
- import android .graphics .BitmapFactory ;
9
- import android .media .MediaPlayer ;
10
8
import android .net .Uri ;
11
9
import android .os .Binder ;
12
10
import android .os .Build ;
13
11
import android .os .IBinder ;
14
12
import android .os .PowerManager ;
15
13
16
14
import androidx .annotation .Nullable ;
17
- import androidx .core .app .NotificationCompat ;
18
15
19
16
import com .getcapacitor .JSObject ;
20
17
import com .getcapacitor .PluginCall ;
21
18
import com .github .nicorac .bcrgui .MainActivity ;
22
- import com .github .nicorac .bcrgui .R ;
23
19
24
20
import java .util .HashMap ;
25
21
26
22
public class AudioPlayerService extends Service {
27
23
28
- private static final String ERR_BAD_ID = "Unexisting player with this 'id'" ;
24
+ private static final String ERR_BAD_ID = "Can't find a player instance with this 'id'" ;
29
25
30
26
private static final String NOTIFICATION_CHANNEL_ID = "BCR-GUI" ;
31
27
private static final String NOTIFICATION_CHANNEL_NAME = "BCR-GUI - Play status" ;
28
+ private static PendingIntent bringAppToForegroundIntent ;
29
+ private static NotificationManager notificationManager ;
32
30
33
31
// wakelock to keep the service alive when playing
34
32
private PowerManager .WakeLock wakeLock = null ;
35
33
36
34
// players collection
37
- private final HashMap <Integer , MediaPlayerInstance > players = new HashMap <>();
35
+ private final HashMap <Integer , MediaPlayerEx > players = new HashMap <>();
38
36
39
37
// reference to plugin
40
38
private IJSEventSender plugin ;
@@ -50,21 +48,52 @@ public AudioPlayerService getService(IJSEventSender plugin) {
50
48
51
49
@ Override
52
50
public void onCreate () {
51
+
53
52
super .onCreate ();
54
53
var powerManager = (PowerManager ) getSystemService (POWER_SERVICE );
55
54
wakeLock = powerManager .newWakeLock (PowerManager .PARTIAL_WAKE_LOCK , "AudioPlayerService::WakelockTag" );
55
+
56
+ // Create an Intent for the "bring-to-front" action to be linked in notifications
57
+ Intent customIntent = new Intent (getApplicationContext (), MainActivity .class );
58
+ customIntent .setAction (Intent .ACTION_MAIN );
59
+ customIntent .addCategory (Intent .CATEGORY_LAUNCHER );
60
+
61
+ // get instance of NotificationManager and create notifications channel
62
+ if (notificationManager == null ) {
63
+ notificationManager = (NotificationManager ) getApplicationContext ().getSystemService (NOTIFICATION_SERVICE );
64
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
65
+ var channel = new NotificationChannel (
66
+ NOTIFICATION_CHANNEL_ID ,
67
+ NOTIFICATION_CHANNEL_NAME ,
68
+ NotificationManager .IMPORTANCE_DEFAULT
69
+ );
70
+ notificationManager .createNotificationChannel (channel );
71
+ }
72
+ }
73
+
74
+ // Create a PendingIntent
75
+ bringAppToForegroundIntent = PendingIntent .getActivity (
76
+ getApplicationContext (), 0 , customIntent ,
77
+ PendingIntent .FLAG_UPDATE_CURRENT | PendingIntent .FLAG_IMMUTABLE
78
+ );
79
+
56
80
}
57
81
58
82
@ Override
59
83
public void onDestroy () {
60
84
super .onDestroy ();
61
- for (MediaPlayerInstance i : players .values ()) {
62
- destroyPlayer (i );
85
+ for (MediaPlayerEx i : players .values ()) {
86
+ release (i );
63
87
}
64
88
players .clear ();
65
89
wakeLockUpdate ();
66
90
}
67
91
92
+ @ Override
93
+ public void onTaskRemoved (Intent rootIntent ) {
94
+ // cancel all notifications
95
+ }
96
+
68
97
@ Override
69
98
public IBinder onBind (Intent intent ) {
70
99
return binder ;
@@ -92,23 +121,34 @@ public void init(PluginCall call) {
92
121
try {
93
122
94
123
var fileUri = Uri .parse (fileUriStr );
95
- var mediaPlayer = MediaPlayer .create (this , fileUri );
96
- mediaPlayer .setLooping (false );
97
- var mpi = new MediaPlayerInstance (
98
- id ,
99
- mediaPlayer ,
124
+ var mpe = new MediaPlayerEx (
125
+ getApplicationContext (),
126
+ fileUri ,
100
127
notificationTitle ,
101
- notificationText
128
+ notificationText ,
129
+ new OnEventListener () {
130
+
131
+ @ Override
132
+ public PendingIntent getNotificationClickIntent () { return bringAppToForegroundIntent ; }
133
+
134
+ @ Override
135
+ public void onUpdate (MediaPlayerEx player ) {
136
+ var res = new JSObject ();
137
+ res .put ("id" , id );
138
+ res .put ("position" , player .getCurrentPosition ());
139
+ plugin .sendJSEvent ("update" , res );
140
+ }
141
+
142
+ @ Override
143
+ public void onCompletion (MediaPlayerEx mp ) {
144
+ var res = new JSObject ();
145
+ res .put ("id" , id );
146
+ plugin .sendJSEvent ("playCompleted" , res );
147
+ }
148
+
149
+ }
102
150
);
103
- players .put (id , mpi );
104
-
105
- // Set completion listener
106
- mediaPlayer .setOnCompletionListener (mp -> {
107
- cancelMediaNotification (mpi );
108
- var res = new JSObject ();
109
- res .put ("id" , id );
110
- plugin .sendJSEvent ("playCompleted" , res );
111
- });
151
+ players .put (id , mpe );
112
152
113
153
} catch (Exception e ) {
114
154
call .reject ("Error loading audio file: " + fileUriStr );
@@ -124,22 +164,24 @@ public void init(PluginCall call) {
124
164
}
125
165
126
166
/**
127
- * Release current audio file and free linked mediaplayer
167
+ * Release current audio file and free the linked mediaplayer
128
168
*/
129
169
public void release (PluginCall call ) {
130
170
131
171
var id = getPlayerId (call );
132
- if (id == null ) return ;
172
+ if (id == null ) {
173
+ call .reject (ERR_BAD_ID );
174
+ return ;
175
+ };
133
176
134
177
// remove item
135
- var i = players .get (id );
136
- if (i == null ) {
178
+ var p = players .get (id );
179
+ if (p == null ) {
137
180
call .reject (ERR_BAD_ID );
138
181
return ;
139
182
}
140
183
else {
141
- destroyPlayer (i );
142
- players .remove (id );
184
+ release (p );
143
185
}
144
186
145
187
call .resolve ();
@@ -149,10 +191,11 @@ public void release(PluginCall call) {
149
191
/**
150
192
* Destroy a MediaPlayer instance
151
193
*/
152
- private void destroyPlayer ( MediaPlayerInstance i ) {
194
+ private void release ( MediaPlayerEx p ) {
153
195
try {
154
- stop (i );
155
- i .player .release ();
196
+ stop (p );
197
+ p .release ();
198
+ players .remove (p .id );
156
199
}
157
200
catch (Exception ignored ) {}
158
201
}
@@ -163,18 +206,17 @@ private void destroyPlayer(MediaPlayerInstance i) {
163
206
public void play (PluginCall call ) {
164
207
165
208
// get target player
166
- var i = getPlayerInstance (call );
167
- if (i == null ) return ;
209
+ var p = getPlayerInstance (call );
210
+ if (p == null ) return ;
168
211
169
212
// test if a position has been passed
170
213
var position = call .getInt ("position" );
171
214
if (position != null ) {
172
- i . player .seekTo (position );
215
+ p .seekTo (position );
173
216
}
174
- if (!i . player .isPlaying ()) {
175
- i . player .start ();
217
+ if (!p .isPlaying ()) {
218
+ p .start ();
176
219
wakeLockUpdate ();
177
- showMediaNotification (i );
178
220
}
179
221
call .resolve ();
180
222
@@ -189,9 +231,8 @@ public void pause(PluginCall call) {
189
231
var i = getPlayerInstance (call );
190
232
if (i == null ) return ;
191
233
192
- if (i .player .isPlaying ()) {
193
- i .player .pause ();
194
- cancelMediaNotification (i );
234
+ if (i .isPlaying ()) {
235
+ i .pause ();
195
236
wakeLockUpdate ();
196
237
}
197
238
call .resolve ();
@@ -208,11 +249,10 @@ public void stop(PluginCall call) {
208
249
stop (i );
209
250
call .resolve ();
210
251
}
211
- public void stop (MediaPlayerInstance i ) {
252
+ public void stop (MediaPlayerEx p ) {
212
253
213
- if (i .player .isPlaying ()) {
214
- i .player .stop ();
215
- cancelMediaNotification (i );
254
+ if (p .isPlaying ()) {
255
+ p .stop ();
216
256
wakeLockUpdate ();
217
257
}
218
258
@@ -228,7 +268,7 @@ public void getDuration(PluginCall call) {
228
268
if (i == null ) return ;
229
269
230
270
var res = new JSObject ();
231
- res .put ("duration" , i .player . getDuration ());
271
+ res .put ("duration" , i .getDuration ());
232
272
call .resolve (res );
233
273
234
274
}
@@ -243,7 +283,7 @@ public void getCurrentTime(PluginCall call) {
243
283
if (i == null ) return ;
244
284
245
285
var res = new JSObject ();
246
- res .put ("currentTime" , i .player . getCurrentPosition ());
286
+ res .put ("currentTime" , i .getCurrentPosition ());
247
287
call .resolve (res );
248
288
249
289
}
@@ -258,7 +298,7 @@ private void wakeLockUpdate() {
258
298
var toBeEnabled = false ;
259
299
try {
260
300
for (var i : players .values ()) {
261
- if (i .player . isPlaying ()) {
301
+ if (i .isPlaying ()) {
262
302
toBeEnabled = true ;
263
303
break ;
264
304
}
@@ -288,7 +328,7 @@ private Integer getPlayerId(PluginCall call) {
288
328
* Return the existing media player instance from id
289
329
*/
290
330
@ Nullable
291
- private MediaPlayerInstance getPlayerInstance (PluginCall call ) {
331
+ private MediaPlayerEx getPlayerInstance (PluginCall call ) {
292
332
293
333
var id = getPlayerId (call );
294
334
if (id == null ) return null ;
@@ -302,61 +342,4 @@ private MediaPlayerInstance getPlayerInstance(PluginCall call) {
302
342
303
343
}
304
344
305
- /**
306
- * Show multimedia notification
307
- */
308
- public void showMediaNotification (MediaPlayerInstance playerInstance ) {
309
-
310
- // Notification manager service
311
- var notificationManager = (NotificationManager ) getApplicationContext ().getSystemService (NOTIFICATION_SERVICE );
312
- if (notificationManager == null ) {
313
- return ;
314
- }
315
-
316
- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
317
- var channel = new NotificationChannel (
318
- NOTIFICATION_CHANNEL_ID ,
319
- NOTIFICATION_CHANNEL_NAME ,
320
- NotificationManager .IMPORTANCE_DEFAULT
321
- );
322
- notificationManager .createNotificationChannel (channel );
323
- }
324
-
325
- // Create an Intent for the "bring-to-front"" action
326
- Intent customIntent = new Intent (getApplicationContext (), MainActivity .class );
327
- customIntent .setAction (Intent .ACTION_MAIN );
328
- customIntent .addCategory (Intent .CATEGORY_LAUNCHER );
329
-
330
- // Create a PendingIntent
331
- PendingIntent pendingIntent = PendingIntent .getActivity (
332
- getApplicationContext (), 0 , customIntent ,
333
- PendingIntent .FLAG_UPDATE_CURRENT | PendingIntent .FLAG_IMMUTABLE
334
- );
335
-
336
- var builder = new NotificationCompat .Builder (getApplicationContext (), NOTIFICATION_CHANNEL_ID )
337
- .setContentTitle (playerInstance .title )
338
- .setContentText (playerInstance .text )
339
- .setSmallIcon (R .drawable .notification )
340
- .setLargeIcon (BitmapFactory .decodeResource (getResources (), R .drawable .notification ))
341
- .setPriority (NotificationCompat .PRIORITY_DEFAULT )
342
- .setContentIntent (pendingIntent ) // Set the PendingIntent
343
- .setOngoing (true ) // Set as "persistant"
344
- ;
345
- var notification = builder .build ();
346
- notificationManager .notify (playerInstance .id , notification );
347
-
348
- }
349
- /**
350
- * Cancel existing multimedia notification
351
- */
352
- public void cancelMediaNotification (MediaPlayerInstance playerInstance ) {
353
-
354
- // Notification manager service
355
- var notificationManager = (NotificationManager ) getApplicationContext ().getSystemService (NOTIFICATION_SERVICE );
356
- if (notificationManager != null ) {
357
- notificationManager .cancel (playerInstance .id );
358
- }
359
-
360
- }
361
-
362
345
}
0 commit comments