@capgo/capacitor-native-audio 8.4.4 → 8.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +1 -1
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +2 -6
- package/android/src/main/java/ee/forgr/audio/PlayerThread.java +62 -0
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +68 -86
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +42 -45
- package/ios/Sources/NativeAudioPlugin/Plugin.swift +1 -1
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -82,7 +82,7 @@ dependencies {
|
|
|
82
82
|
implementation 'androidx.media3:media3-transformer:1.10.0'
|
|
83
83
|
implementation 'androidx.media3:media3-ui:1.10.0'
|
|
84
84
|
implementation 'androidx.media3:media3-database:1.10.0'
|
|
85
|
-
implementation 'androidx.media3:media3-common:1.10.
|
|
85
|
+
implementation 'androidx.media3:media3-common:1.10.1'
|
|
86
86
|
// Media notification support
|
|
87
87
|
implementation 'androidx.media:media:1.7.1'
|
|
88
88
|
implementation 'androidx.core:core:1.13.1'
|
|
@@ -729,8 +729,6 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
729
729
|
if (audioAssetList.containsKey(audioId)) {
|
|
730
730
|
AudioAsset asset = audioAssetList.get(audioId);
|
|
731
731
|
if (asset != null) {
|
|
732
|
-
boolean wasPlaying = asset.isPlaying();
|
|
733
|
-
|
|
734
732
|
JSObject data = getAudioAssetData(audioId);
|
|
735
733
|
data.put("volumeBeforePause", asset.getVolume());
|
|
736
734
|
setAudioAssetData(audioId, data);
|
|
@@ -741,9 +739,7 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
741
739
|
asset.pause();
|
|
742
740
|
}
|
|
743
741
|
|
|
744
|
-
|
|
745
|
-
resumeList.add(asset);
|
|
746
|
-
}
|
|
742
|
+
resumeList.removeIf((candidate) -> candidate == asset);
|
|
747
743
|
|
|
748
744
|
updateTrackedPlaybackState(audioId, PlaybackStateCompat.STATE_PAUSED);
|
|
749
745
|
|
|
@@ -792,7 +788,7 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
792
788
|
|
|
793
789
|
data.remove("volumeBeforePause");
|
|
794
790
|
setAudioAssetData(audioId, data);
|
|
795
|
-
resumeList.
|
|
791
|
+
resumeList.removeIf((candidate) -> candidate == asset);
|
|
796
792
|
updateTrackedPlaybackState(audioId, PlaybackStateCompat.STATE_PLAYING);
|
|
797
793
|
|
|
798
794
|
// Update notification when resumed
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package ee.forgr.audio;
|
|
2
|
+
|
|
3
|
+
import android.os.Handler;
|
|
4
|
+
import android.os.Looper;
|
|
5
|
+
import java.util.concurrent.CountDownLatch;
|
|
6
|
+
import java.util.concurrent.TimeUnit;
|
|
7
|
+
|
|
8
|
+
final class PlayerThread {
|
|
9
|
+
|
|
10
|
+
private PlayerThread() {}
|
|
11
|
+
|
|
12
|
+
static void run(PlayerRunnable runnable) throws Exception {
|
|
13
|
+
call(() -> {
|
|
14
|
+
runnable.run();
|
|
15
|
+
return null;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@SuppressWarnings("unchecked")
|
|
20
|
+
static <T> T call(PlayerCallable<T> callable) throws Exception {
|
|
21
|
+
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
22
|
+
return callable.call();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
final CountDownLatch latch = new CountDownLatch(1);
|
|
26
|
+
final Object[] result = new Object[1];
|
|
27
|
+
final Exception[] error = new Exception[1];
|
|
28
|
+
|
|
29
|
+
new Handler(Looper.getMainLooper()).post(() -> {
|
|
30
|
+
try {
|
|
31
|
+
result[0] = callable.call();
|
|
32
|
+
} catch (Exception e) {
|
|
33
|
+
error[0] = e;
|
|
34
|
+
} finally {
|
|
35
|
+
latch.countDown();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
if (!latch.await(2, TimeUnit.SECONDS)) {
|
|
41
|
+
throw new Exception("Timed out waiting for ExoPlayer operation on main thread");
|
|
42
|
+
}
|
|
43
|
+
} catch (InterruptedException e) {
|
|
44
|
+
Thread.currentThread().interrupt();
|
|
45
|
+
throw new Exception("Interrupted waiting for ExoPlayer operation on main thread", e);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (error[0] != null) {
|
|
49
|
+
throw error[0];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (T) result[0];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface PlayerRunnable {
|
|
56
|
+
void run() throws Exception;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface PlayerCallable<T> {
|
|
60
|
+
T call() throws Exception;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -233,44 +233,30 @@ public class RemoteAudioAsset extends AudioAsset {
|
|
|
233
233
|
|
|
234
234
|
@Override
|
|
235
235
|
public boolean pause() throws Exception {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
for (ExoPlayer player : players) {
|
|
245
|
-
if (player != null && player.isPlaying()) {
|
|
246
|
-
player.pause();
|
|
247
|
-
stopCurrentTimeUpdates();
|
|
248
|
-
wasPlaying[0] = true;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
236
|
+
return PlayerThread.call(() -> {
|
|
237
|
+
boolean wasPlaying = false;
|
|
238
|
+
cancelFade();
|
|
239
|
+
for (ExoPlayer player : players) {
|
|
240
|
+
if (player != null && player.isPlaying()) {
|
|
241
|
+
player.pause();
|
|
242
|
+
stopCurrentTimeUpdates();
|
|
243
|
+
wasPlaying = true;
|
|
252
244
|
}
|
|
253
|
-
|
|
254
|
-
|
|
245
|
+
}
|
|
246
|
+
return wasPlaying;
|
|
247
|
+
});
|
|
255
248
|
}
|
|
256
249
|
|
|
257
250
|
@Override
|
|
258
251
|
public void resume() throws Exception {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
@Override
|
|
264
|
-
public void run() {
|
|
265
|
-
for (ExoPlayer player : players) {
|
|
266
|
-
if (player != null && !player.isPlaying()) {
|
|
267
|
-
player.play();
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
startCurrentTimeUpdates();
|
|
271
|
-
}
|
|
252
|
+
PlayerThread.run(() -> {
|
|
253
|
+
for (ExoPlayer player : players) {
|
|
254
|
+
if (player != null && !player.isPlaying()) {
|
|
255
|
+
player.play();
|
|
272
256
|
}
|
|
273
|
-
|
|
257
|
+
}
|
|
258
|
+
startCurrentTimeUpdates();
|
|
259
|
+
});
|
|
274
260
|
}
|
|
275
261
|
|
|
276
262
|
@Override
|
|
@@ -390,79 +376,75 @@ public class RemoteAudioAsset extends AudioAsset {
|
|
|
390
376
|
|
|
391
377
|
@Override
|
|
392
378
|
public float getVolume() throws Exception {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
379
|
+
return PlayerThread.call(() -> {
|
|
380
|
+
if (players.isEmpty()) {
|
|
381
|
+
throw new Exception("No ExoPlayer available");
|
|
382
|
+
}
|
|
396
383
|
|
|
397
|
-
|
|
398
|
-
|
|
384
|
+
final ExoPlayer player = players.get(playIndex);
|
|
385
|
+
return player != null ? player.getVolume() : 0f;
|
|
386
|
+
});
|
|
399
387
|
}
|
|
400
388
|
|
|
401
389
|
@Override
|
|
402
390
|
public boolean isPlaying() throws Exception {
|
|
403
|
-
|
|
391
|
+
return PlayerThread.call(() -> {
|
|
392
|
+
if (players.isEmpty() || !isPrepared) return false;
|
|
404
393
|
|
|
405
|
-
|
|
406
|
-
|
|
394
|
+
ExoPlayer player = players.get(playIndex);
|
|
395
|
+
return player != null && player.isPlaying();
|
|
396
|
+
});
|
|
407
397
|
}
|
|
408
398
|
|
|
409
399
|
@Override
|
|
410
400
|
public double getDuration() {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
logger.debug("Raw duration: " + rawDuration + ", TIME_UNSET=" + androidx.media3.common.C.TIME_UNSET);
|
|
426
|
-
if (rawDuration != androidx.media3.common.C.TIME_UNSET) {
|
|
427
|
-
duration[0] = rawDuration / 1000.0;
|
|
428
|
-
logger.debug("Final duration in seconds: " + duration[0]);
|
|
429
|
-
} else {
|
|
430
|
-
logger.debug("Duration is TIME_UNSET");
|
|
431
|
-
}
|
|
432
|
-
} else {
|
|
433
|
-
logger.debug("Player not in READY state");
|
|
434
|
-
}
|
|
401
|
+
try {
|
|
402
|
+
return PlayerThread.call(() -> {
|
|
403
|
+
logger.debug("getDuration called, players empty: " + players.isEmpty() + ", isPrepared: " + isPrepared);
|
|
404
|
+
if (!players.isEmpty() && isPrepared) {
|
|
405
|
+
ExoPlayer player = players.get(playIndex);
|
|
406
|
+
int state = player.getPlaybackState();
|
|
407
|
+
logger.debug("Player state: " + state + " (READY=" + Player.STATE_READY + ")");
|
|
408
|
+
if (state == Player.STATE_READY) {
|
|
409
|
+
long rawDuration = player.getDuration();
|
|
410
|
+
logger.debug("Raw duration: " + rawDuration + ", TIME_UNSET=" + androidx.media3.common.C.TIME_UNSET);
|
|
411
|
+
if (rawDuration != androidx.media3.common.C.TIME_UNSET) {
|
|
412
|
+
double duration = rawDuration / 1000.0;
|
|
413
|
+
logger.debug("Final duration in seconds: " + duration);
|
|
414
|
+
return duration;
|
|
435
415
|
}
|
|
416
|
+
logger.debug("Duration is TIME_UNSET");
|
|
417
|
+
} else {
|
|
418
|
+
logger.debug("Player not in READY state");
|
|
436
419
|
}
|
|
437
|
-
|
|
438
|
-
|
|
420
|
+
}
|
|
421
|
+
logger.debug("No players or not prepared for duration");
|
|
422
|
+
return 0.0;
|
|
423
|
+
});
|
|
424
|
+
} catch (Exception e) {
|
|
425
|
+
logger.error("Error getting duration", e);
|
|
426
|
+
return 0;
|
|
439
427
|
}
|
|
440
|
-
logger.debug("No players or not prepared for duration");
|
|
441
|
-
return 0;
|
|
442
428
|
}
|
|
443
429
|
|
|
444
430
|
@Override
|
|
445
431
|
public double getCurrentPosition() {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
ExoPlayer player = players.get(playIndex);
|
|
455
|
-
if (player.getPlaybackState() == Player.STATE_READY) {
|
|
456
|
-
long rawPosition = player.getCurrentPosition();
|
|
457
|
-
logger.debug("Raw position: " + rawPosition);
|
|
458
|
-
position[0] = rawPosition / 1000.0;
|
|
459
|
-
}
|
|
460
|
-
}
|
|
432
|
+
try {
|
|
433
|
+
return PlayerThread.call(() -> {
|
|
434
|
+
if (!players.isEmpty() && isPrepared) {
|
|
435
|
+
ExoPlayer player = players.get(playIndex);
|
|
436
|
+
if (player.getPlaybackState() == Player.STATE_READY) {
|
|
437
|
+
long rawPosition = player.getCurrentPosition();
|
|
438
|
+
logger.debug("Raw position: " + rawPosition);
|
|
439
|
+
return rawPosition / 1000.0;
|
|
461
440
|
}
|
|
462
|
-
|
|
463
|
-
|
|
441
|
+
}
|
|
442
|
+
return 0.0;
|
|
443
|
+
});
|
|
444
|
+
} catch (Exception e) {
|
|
445
|
+
logger.error("Error getting current position", e);
|
|
446
|
+
return 0;
|
|
464
447
|
}
|
|
465
|
-
return 0;
|
|
466
448
|
}
|
|
467
449
|
|
|
468
450
|
@Override
|
|
@@ -185,28 +185,25 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
185
185
|
|
|
186
186
|
@Override
|
|
187
187
|
public boolean pause() throws Exception {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
return wasPlaying[0];
|
|
188
|
+
return PlayerThread.call(() -> {
|
|
189
|
+
cancelFade();
|
|
190
|
+
if (player != null && player.isPlaying()) {
|
|
191
|
+
player.setPlayWhenReady(false);
|
|
192
|
+
stopCurrentTimeUpdates();
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
});
|
|
200
197
|
}
|
|
201
198
|
|
|
202
199
|
@Override
|
|
203
200
|
public void resume() throws Exception {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.runOnUiThread(() -> {
|
|
201
|
+
PlayerThread.run(() -> {
|
|
202
|
+
if (player != null) {
|
|
207
203
|
player.setPlayWhenReady(true);
|
|
208
204
|
startCurrentTimeUpdates();
|
|
209
|
-
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
210
207
|
}
|
|
211
208
|
|
|
212
209
|
@Override
|
|
@@ -323,50 +320,50 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
323
320
|
|
|
324
321
|
@Override
|
|
325
322
|
public float getVolume() throws Exception {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
323
|
+
return PlayerThread.call(() -> {
|
|
324
|
+
if (player != null) {
|
|
325
|
+
return player.getVolume();
|
|
326
|
+
}
|
|
327
|
+
return 0f;
|
|
328
|
+
});
|
|
330
329
|
}
|
|
331
330
|
|
|
332
331
|
@Override
|
|
333
332
|
public boolean isPlaying() throws Exception {
|
|
334
|
-
return player != null && player.isPlaying();
|
|
333
|
+
return PlayerThread.call(() -> player != null && player.isPlaying());
|
|
335
334
|
}
|
|
336
335
|
|
|
337
336
|
@Override
|
|
338
337
|
public double getDuration() {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
long rawDuration = player.getDuration();
|
|
346
|
-
if (rawDuration != androidx.media3.common.C.TIME_UNSET) {
|
|
347
|
-
duration[0] = rawDuration / 1000.0;
|
|
348
|
-
}
|
|
338
|
+
try {
|
|
339
|
+
return PlayerThread.call(() -> {
|
|
340
|
+
if (isPrepared && player != null && player.getPlaybackState() == Player.STATE_READY) {
|
|
341
|
+
long rawDuration = player.getDuration();
|
|
342
|
+
if (rawDuration != androidx.media3.common.C.TIME_UNSET) {
|
|
343
|
+
return rawDuration / 1000.0;
|
|
349
344
|
}
|
|
350
|
-
}
|
|
351
|
-
|
|
345
|
+
}
|
|
346
|
+
return 0.0;
|
|
347
|
+
});
|
|
348
|
+
} catch (Exception e) {
|
|
349
|
+
logger.error("Error getting duration", e);
|
|
350
|
+
return 0;
|
|
352
351
|
}
|
|
353
|
-
return 0;
|
|
354
352
|
}
|
|
355
353
|
|
|
356
354
|
@Override
|
|
357
355
|
public double getCurrentPosition() {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return
|
|
356
|
+
try {
|
|
357
|
+
return PlayerThread.call(() -> {
|
|
358
|
+
if (isPrepared && player != null && player.getPlaybackState() == Player.STATE_READY) {
|
|
359
|
+
return player.getCurrentPosition() / 1000.0;
|
|
360
|
+
}
|
|
361
|
+
return 0.0;
|
|
362
|
+
});
|
|
363
|
+
} catch (Exception e) {
|
|
364
|
+
logger.error("Error getting current position", e);
|
|
365
|
+
return 0;
|
|
368
366
|
}
|
|
369
|
-
return 0;
|
|
370
367
|
}
|
|
371
368
|
|
|
372
369
|
@Override
|
|
@@ -18,7 +18,7 @@ private enum PlaybackStateValue: String {
|
|
|
18
18
|
@objc(NativeAudio)
|
|
19
19
|
// swiftlint:disable:next type_body_length
|
|
20
20
|
public class NativeAudio: CAPPlugin, AVAudioPlayerDelegate, CAPBridgedPlugin {
|
|
21
|
-
private let pluginVersion: String = "8.4.
|
|
21
|
+
private let pluginVersion: String = "8.4.5"
|
|
22
22
|
public let identifier = "NativeAudio"
|
|
23
23
|
public let jsName = "NativeAudio"
|
|
24
24
|
public let pluginMethods: [CAPPluginMethod] = [
|