@capgo/native-audio 8.2.12 → 8.2.14
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/README.md +147 -34
- package/android/build.gradle +1 -1
- package/android/src/main/java/ee/forgr/audio/AudioAsset.java +352 -74
- package/android/src/main/java/ee/forgr/audio/AudioDispatcher.java +24 -3
- package/android/src/main/java/ee/forgr/audio/Constant.java +9 -1
- package/android/src/main/java/ee/forgr/audio/Logger.java +55 -0
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +336 -57
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +307 -98
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +285 -96
- package/dist/docs.json +307 -41
- package/dist/esm/definitions.d.ts +116 -38
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +52 -41
- package/dist/esm/web.js +386 -41
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +386 -41
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +386 -41
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeAudioPlugin/AudioAsset+Fade.swift +104 -0
- package/ios/Sources/NativeAudioPlugin/AudioAsset.swift +168 -324
- package/ios/Sources/NativeAudioPlugin/Constant.swift +17 -4
- package/ios/Sources/NativeAudioPlugin/Logger.swift +43 -0
- package/ios/Sources/NativeAudioPlugin/Plugin.swift +176 -87
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset+Fade.swift +110 -0
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset.swift +117 -273
- package/ios/Tests/NativeAudioPluginTests/PluginTests.swift +47 -72
- package/package.json +1 -1
|
@@ -3,16 +3,25 @@ package ee.forgr.audio;
|
|
|
3
3
|
import static ee.forgr.audio.Constant.ASSET_ID;
|
|
4
4
|
import static ee.forgr.audio.Constant.ASSET_PATH;
|
|
5
5
|
import static ee.forgr.audio.Constant.AUDIO_CHANNEL_NUM;
|
|
6
|
+
import static ee.forgr.audio.Constant.DELAY;
|
|
7
|
+
import static ee.forgr.audio.Constant.DURATION;
|
|
6
8
|
import static ee.forgr.audio.Constant.ERROR_ASSET_NOT_LOADED;
|
|
7
9
|
import static ee.forgr.audio.Constant.ERROR_ASSET_PATH_MISSING;
|
|
8
10
|
import static ee.forgr.audio.Constant.ERROR_AUDIO_ASSET_MISSING;
|
|
9
11
|
import static ee.forgr.audio.Constant.ERROR_AUDIO_EXISTS;
|
|
10
12
|
import static ee.forgr.audio.Constant.ERROR_AUDIO_ID_MISSING;
|
|
13
|
+
import static ee.forgr.audio.Constant.FADE_IN;
|
|
14
|
+
import static ee.forgr.audio.Constant.FADE_IN_DURATION;
|
|
15
|
+
import static ee.forgr.audio.Constant.FADE_OUT;
|
|
16
|
+
import static ee.forgr.audio.Constant.FADE_OUT_DURATION;
|
|
17
|
+
import static ee.forgr.audio.Constant.FADE_OUT_START_TIME;
|
|
11
18
|
import static ee.forgr.audio.Constant.LOOP;
|
|
12
19
|
import static ee.forgr.audio.Constant.NOTIFICATION_METADATA;
|
|
13
20
|
import static ee.forgr.audio.Constant.OPT_FOCUS_AUDIO;
|
|
21
|
+
import static ee.forgr.audio.Constant.PLAY;
|
|
14
22
|
import static ee.forgr.audio.Constant.RATE;
|
|
15
23
|
import static ee.forgr.audio.Constant.SHOW_NOTIFICATION;
|
|
24
|
+
import static ee.forgr.audio.Constant.TIME;
|
|
16
25
|
import static ee.forgr.audio.Constant.VOLUME;
|
|
17
26
|
|
|
18
27
|
import android.Manifest;
|
|
@@ -55,6 +64,8 @@ import java.util.Iterator;
|
|
|
55
64
|
import java.util.Map;
|
|
56
65
|
import java.util.Set;
|
|
57
66
|
import java.util.UUID;
|
|
67
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
68
|
+
import java.util.concurrent.CopyOnWriteArrayList;
|
|
58
69
|
|
|
59
70
|
@UnstableApi
|
|
60
71
|
@CapacitorPlugin(
|
|
@@ -69,19 +80,23 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
69
80
|
private final String pluginVersion = "";
|
|
70
81
|
|
|
71
82
|
public static final String TAG = "NativeAudio";
|
|
83
|
+
private static final Logger logger = new Logger(TAG);
|
|
84
|
+
public static boolean debugEnabled = false;
|
|
72
85
|
|
|
73
|
-
private static
|
|
74
|
-
private static
|
|
86
|
+
private static ConcurrentHashMap<String, AudioAsset> audioAssetList = new ConcurrentHashMap<>();
|
|
87
|
+
private static CopyOnWriteArrayList<AudioAsset> resumeList;
|
|
75
88
|
private AudioManager audioManager;
|
|
76
|
-
private boolean fadeMusic = false;
|
|
77
89
|
private boolean audioFocusRequested = false;
|
|
78
90
|
private int originalAudioMode = AudioManager.MODE_INVALID;
|
|
79
91
|
|
|
80
|
-
private final Map<String, PluginCall> pendingDurationCalls = new
|
|
92
|
+
private final Map<String, PluginCall> pendingDurationCalls = new ConcurrentHashMap<>();
|
|
93
|
+
private final Map<String, Handler> pendingPlayHandlers = new ConcurrentHashMap<>();
|
|
94
|
+
private final Map<String, Runnable> pendingPlayRunnables = new ConcurrentHashMap<>();
|
|
95
|
+
private final Map<String, JSObject> audioData = new ConcurrentHashMap<>();
|
|
81
96
|
|
|
82
97
|
// Notification center support
|
|
83
98
|
private boolean showNotification = false;
|
|
84
|
-
private Map<String, Map<String, String>> notificationMetadataMap = new
|
|
99
|
+
private Map<String, Map<String, String>> notificationMetadataMap = new ConcurrentHashMap<>();
|
|
85
100
|
private MediaSessionCompat mediaSession;
|
|
86
101
|
private String currentlyPlayingAssetId;
|
|
87
102
|
private static final int NOTIFICATION_ID = 1001;
|
|
@@ -103,7 +118,7 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
103
118
|
|
|
104
119
|
this.audioManager = (AudioManager) this.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
|
105
120
|
|
|
106
|
-
audioAssetList = new
|
|
121
|
+
audioAssetList = new ConcurrentHashMap<>();
|
|
107
122
|
|
|
108
123
|
// Store the original audio mode but don't request focus yet
|
|
109
124
|
if (this.audioManager != null) {
|
|
@@ -154,7 +169,7 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
154
169
|
|
|
155
170
|
try {
|
|
156
171
|
if (audioAssetList != null) {
|
|
157
|
-
for (
|
|
172
|
+
for (Map.Entry<String, AudioAsset> entry : audioAssetList.entrySet()) {
|
|
158
173
|
AudioAsset audio = entry.getValue();
|
|
159
174
|
|
|
160
175
|
if (audio != null) {
|
|
@@ -196,6 +211,16 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
196
211
|
}
|
|
197
212
|
}
|
|
198
213
|
|
|
214
|
+
@PluginMethod
|
|
215
|
+
public void setDebugMode(PluginCall call) {
|
|
216
|
+
boolean enabled = Boolean.TRUE.equals(call.getBoolean("enabled", false));
|
|
217
|
+
debugEnabled = enabled;
|
|
218
|
+
if (enabled) {
|
|
219
|
+
logger.info("Debug mode enabled");
|
|
220
|
+
}
|
|
221
|
+
call.resolve();
|
|
222
|
+
}
|
|
223
|
+
|
|
199
224
|
@PluginMethod
|
|
200
225
|
public void configure(PluginCall call) {
|
|
201
226
|
initSoundPool();
|
|
@@ -212,7 +237,6 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
212
237
|
|
|
213
238
|
boolean focus = call.getBoolean(OPT_FOCUS_AUDIO, false);
|
|
214
239
|
boolean background = call.getBoolean("background", false);
|
|
215
|
-
this.fadeMusic = call.getBoolean("fade", false);
|
|
216
240
|
this.showNotification = call.getBoolean(SHOW_NOTIFICATION, false);
|
|
217
241
|
this.backgroundPlayback = call.getBoolean("backgroundPlayback", false);
|
|
218
242
|
|
|
@@ -478,12 +502,125 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
478
502
|
new Runnable() {
|
|
479
503
|
@Override
|
|
480
504
|
public void run() {
|
|
481
|
-
|
|
505
|
+
try {
|
|
506
|
+
final String audioId = call.getString(ASSET_ID);
|
|
507
|
+
if (!isStringValid(audioId)) {
|
|
508
|
+
call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
final double time = call.getDouble(TIME, 0.0);
|
|
513
|
+
final double delaySecs = call.getDouble(DELAY, 0.0);
|
|
514
|
+
final float volume = call.getFloat(VOLUME, 1F);
|
|
515
|
+
final boolean fadeIn = call.getBoolean(FADE_IN, false);
|
|
516
|
+
final double fadeInDurationMs =
|
|
517
|
+
call.getDouble(FADE_IN_DURATION, AudioAsset.DEFAULT_FADE_DURATION_MS / 1000.0) * 1000.0;
|
|
518
|
+
final boolean fadeOut = call.getBoolean(FADE_OUT, false);
|
|
519
|
+
final double fadeOutDurationMs =
|
|
520
|
+
call.getDouble(FADE_OUT_DURATION, AudioAsset.DEFAULT_FADE_DURATION_MS / 1000.0) * 1000.0;
|
|
521
|
+
final double fadeOutStartTimeSecs = call.getDouble(FADE_OUT_START_TIME, 0.0);
|
|
522
|
+
|
|
523
|
+
cancelPendingPlay(audioId);
|
|
524
|
+
clearFadeOutToStopTimer(audioId);
|
|
525
|
+
|
|
526
|
+
if (delaySecs > 0) {
|
|
527
|
+
final Handler handler = new Handler(Looper.getMainLooper());
|
|
528
|
+
final Runnable runnable = new Runnable() {
|
|
529
|
+
@Override
|
|
530
|
+
public void run() {
|
|
531
|
+
pendingPlayHandlers.remove(audioId);
|
|
532
|
+
pendingPlayRunnables.remove(audioId);
|
|
533
|
+
executePlay(
|
|
534
|
+
call,
|
|
535
|
+
audioId,
|
|
536
|
+
time,
|
|
537
|
+
volume,
|
|
538
|
+
fadeIn,
|
|
539
|
+
fadeInDurationMs,
|
|
540
|
+
fadeOut,
|
|
541
|
+
fadeOutDurationMs,
|
|
542
|
+
fadeOutStartTimeSecs
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
pendingPlayHandlers.put(audioId, handler);
|
|
547
|
+
pendingPlayRunnables.put(audioId, runnable);
|
|
548
|
+
handler.postDelayed(runnable, Math.max(0L, (long) (delaySecs * 1000)));
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
executePlay(
|
|
553
|
+
call,
|
|
554
|
+
audioId,
|
|
555
|
+
time,
|
|
556
|
+
volume,
|
|
557
|
+
fadeIn,
|
|
558
|
+
fadeInDurationMs,
|
|
559
|
+
fadeOut,
|
|
560
|
+
fadeOutDurationMs,
|
|
561
|
+
fadeOutStartTimeSecs
|
|
562
|
+
);
|
|
563
|
+
} catch (Exception ex) {
|
|
564
|
+
call.reject(ex.getMessage());
|
|
565
|
+
}
|
|
482
566
|
}
|
|
483
567
|
}
|
|
484
568
|
);
|
|
485
569
|
}
|
|
486
570
|
|
|
571
|
+
private void executePlay(
|
|
572
|
+
PluginCall call,
|
|
573
|
+
String audioId,
|
|
574
|
+
double time,
|
|
575
|
+
float volume,
|
|
576
|
+
boolean fadeIn,
|
|
577
|
+
double fadeInDurationMs,
|
|
578
|
+
boolean fadeOut,
|
|
579
|
+
double fadeOutDurationMs,
|
|
580
|
+
double fadeOutStartTimeSecs
|
|
581
|
+
) {
|
|
582
|
+
try {
|
|
583
|
+
if (!audioAssetList.containsKey(audioId)) {
|
|
584
|
+
call.reject(ERROR_ASSET_NOT_LOADED + " - " + audioId);
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
AudioAsset asset = audioAssetList.get(audioId);
|
|
589
|
+
if (asset == null) {
|
|
590
|
+
call.reject(ERROR_AUDIO_ASSET_MISSING + " - " + audioId);
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (fadeIn) {
|
|
595
|
+
asset.playWithFadeIn(time, volume, fadeInDurationMs);
|
|
596
|
+
} else {
|
|
597
|
+
asset.play(time, volume);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (fadeOut) {
|
|
601
|
+
handleFadeOut(asset, audioId, fadeOutDurationMs, fadeOutStartTimeSecs);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (showNotification) {
|
|
605
|
+
currentlyPlayingAssetId = audioId;
|
|
606
|
+
updateNotification(audioId);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
call.resolve();
|
|
610
|
+
} catch (Exception ex) {
|
|
611
|
+
call.reject(ex.getMessage());
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private void cancelPendingPlay(String audioId) {
|
|
616
|
+
if (audioId == null) return;
|
|
617
|
+
Handler handler = pendingPlayHandlers.remove(audioId);
|
|
618
|
+
Runnable runnable = pendingPlayRunnables.remove(audioId);
|
|
619
|
+
if (handler != null && runnable != null) {
|
|
620
|
+
handler.removeCallbacks(runnable);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
487
624
|
@PluginMethod
|
|
488
625
|
public void getCurrentTime(final PluginCall call) {
|
|
489
626
|
try {
|
|
@@ -517,21 +654,15 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
517
654
|
call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
|
|
518
655
|
return;
|
|
519
656
|
}
|
|
520
|
-
|
|
521
|
-
if (
|
|
522
|
-
|
|
523
|
-
if (
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
ret.put("duration", duration);
|
|
528
|
-
call.resolve(ret);
|
|
529
|
-
} else {
|
|
530
|
-
// Save the call to resolve it later when duration is available
|
|
531
|
-
saveDurationCall(audioId, call);
|
|
532
|
-
}
|
|
657
|
+
AudioAsset asset = audioAssetList.get(audioId);
|
|
658
|
+
if (asset != null) {
|
|
659
|
+
double duration = asset.getDuration();
|
|
660
|
+
if (duration > 0) {
|
|
661
|
+
JSObject ret = new JSObject();
|
|
662
|
+
ret.put("duration", duration);
|
|
663
|
+
call.resolve(ret);
|
|
533
664
|
} else {
|
|
534
|
-
|
|
665
|
+
saveDurationCall(audioId, call);
|
|
535
666
|
}
|
|
536
667
|
} else {
|
|
537
668
|
call.reject(ERROR_ASSET_NOT_LOADED + " - " + audioId);
|
|
@@ -547,6 +678,9 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
547
678
|
new Runnable() {
|
|
548
679
|
@Override
|
|
549
680
|
public void run() {
|
|
681
|
+
String audioId = call.getString(ASSET_ID);
|
|
682
|
+
cancelPendingPlay(audioId);
|
|
683
|
+
clearFadeOutToStopTimer(audioId);
|
|
550
684
|
playOrLoop("loop", call);
|
|
551
685
|
}
|
|
552
686
|
}
|
|
@@ -558,11 +692,25 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
558
692
|
try {
|
|
559
693
|
initSoundPool();
|
|
560
694
|
String audioId = call.getString(ASSET_ID);
|
|
695
|
+
final boolean fadeOut = call.getBoolean(FADE_OUT, false);
|
|
696
|
+
final double fadeOutDurationMs = call.getDouble(FADE_OUT_DURATION, AudioAsset.DEFAULT_FADE_DURATION_MS / 1000.0) * 1000.0;
|
|
697
|
+
|
|
698
|
+
cancelPendingPlay(audioId);
|
|
561
699
|
|
|
562
700
|
if (audioAssetList.containsKey(audioId)) {
|
|
563
701
|
AudioAsset asset = audioAssetList.get(audioId);
|
|
564
702
|
if (asset != null) {
|
|
565
|
-
boolean wasPlaying = asset.
|
|
703
|
+
boolean wasPlaying = asset.isPlaying();
|
|
704
|
+
|
|
705
|
+
JSObject data = getAudioAssetData(audioId);
|
|
706
|
+
data.put("volumeBeforePause", asset.getVolume());
|
|
707
|
+
setAudioAssetData(audioId, data);
|
|
708
|
+
|
|
709
|
+
if (fadeOut) {
|
|
710
|
+
asset.stopWithFade(fadeOutDurationMs, true);
|
|
711
|
+
} else {
|
|
712
|
+
asset.pause();
|
|
713
|
+
}
|
|
566
714
|
|
|
567
715
|
if (wasPlaying) {
|
|
568
716
|
resumeList.add(asset);
|
|
@@ -590,11 +738,26 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
590
738
|
try {
|
|
591
739
|
initSoundPool();
|
|
592
740
|
String audioId = call.getString(ASSET_ID);
|
|
741
|
+
final boolean fadeIn = call.getBoolean(FADE_IN, false);
|
|
742
|
+
final double fadeInDurationMs = call.getDouble(FADE_IN_DURATION, AudioAsset.DEFAULT_FADE_DURATION_MS / 1000.0) * 1000.0;
|
|
593
743
|
|
|
594
744
|
if (audioAssetList.containsKey(audioId)) {
|
|
595
745
|
AudioAsset asset = audioAssetList.get(audioId);
|
|
596
746
|
if (asset != null) {
|
|
597
|
-
|
|
747
|
+
JSObject data = getAudioAssetData(audioId);
|
|
748
|
+
float volumeBeforePause = (float) data.optDouble("volumeBeforePause", asset.getVolume());
|
|
749
|
+
|
|
750
|
+
if (fadeIn) {
|
|
751
|
+
asset.setVolume(0f, 0);
|
|
752
|
+
asset.resume();
|
|
753
|
+
asset.setVolume(volumeBeforePause, fadeInDurationMs);
|
|
754
|
+
} else {
|
|
755
|
+
asset.setVolume(volumeBeforePause, 0);
|
|
756
|
+
asset.resume();
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
data.remove("volumeBeforePause");
|
|
760
|
+
setAudioAssetData(audioId, data);
|
|
598
761
|
resumeList.add(asset);
|
|
599
762
|
|
|
600
763
|
// Update notification when resumed
|
|
@@ -622,11 +785,18 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
622
785
|
public void run() {
|
|
623
786
|
try {
|
|
624
787
|
String audioId = call.getString(ASSET_ID);
|
|
788
|
+
boolean fadeOut = call.getBoolean(FADE_OUT, false);
|
|
789
|
+
double fadeOutDurationMs = call.getDouble(FADE_OUT_DURATION, AudioAsset.DEFAULT_FADE_DURATION_MS / 1000.0) * 1000.0;
|
|
790
|
+
|
|
625
791
|
if (!isStringValid(audioId)) {
|
|
626
792
|
call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
|
|
627
793
|
return;
|
|
628
794
|
}
|
|
629
|
-
|
|
795
|
+
|
|
796
|
+
cancelPendingPlay(audioId);
|
|
797
|
+
clearFadeOutToStopTimer(audioId);
|
|
798
|
+
stopAudio(audioId, fadeOut, fadeOutDurationMs);
|
|
799
|
+
audioData.remove(audioId);
|
|
630
800
|
|
|
631
801
|
// Clear notification when stopped
|
|
632
802
|
if (showNotification) {
|
|
@@ -649,19 +819,18 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
649
819
|
initSoundPool();
|
|
650
820
|
new JSObject();
|
|
651
821
|
JSObject status;
|
|
652
|
-
|
|
653
822
|
if (isStringValid(call.getString(ASSET_ID))) {
|
|
654
823
|
String audioId = call.getString(ASSET_ID);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
824
|
+
cancelPendingPlay(audioId);
|
|
825
|
+
pendingPlayHandlers.remove(audioId);
|
|
826
|
+
pendingPlayRunnables.remove(audioId);
|
|
827
|
+
audioData.remove(audioId);
|
|
828
|
+
AudioAsset asset = audioAssetList.get(audioId);
|
|
829
|
+
if (asset != null) {
|
|
830
|
+
clearFadeOutToStopTimer(audioId);
|
|
831
|
+
asset.unload();
|
|
832
|
+
audioAssetList.remove(audioId);
|
|
833
|
+
call.resolve();
|
|
665
834
|
} else {
|
|
666
835
|
call.reject(ERROR_AUDIO_ASSET_MISSING + " - " + audioId);
|
|
667
836
|
}
|
|
@@ -669,6 +838,12 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
669
838
|
call.reject(ERROR_AUDIO_ID_MISSING);
|
|
670
839
|
}
|
|
671
840
|
} catch (Exception ex) {
|
|
841
|
+
String audioId = call.getString(ASSET_ID);
|
|
842
|
+
if (audioId != null) {
|
|
843
|
+
pendingPlayHandlers.remove(audioId);
|
|
844
|
+
pendingPlayRunnables.remove(audioId);
|
|
845
|
+
audioData.remove(audioId);
|
|
846
|
+
}
|
|
672
847
|
call.reject(ex.getMessage());
|
|
673
848
|
}
|
|
674
849
|
}
|
|
@@ -680,11 +855,19 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
680
855
|
|
|
681
856
|
String audioId = call.getString(ASSET_ID);
|
|
682
857
|
float volume = call.getFloat(VOLUME, 1F);
|
|
858
|
+
double durationSecs = call.getDouble(DURATION, 0.0);
|
|
859
|
+
|
|
860
|
+
if (durationSecs > 0) {
|
|
861
|
+
logger.debug("setVolume " + volume + " over duration " + durationSecs + " seconds");
|
|
862
|
+
} else {
|
|
863
|
+
logger.debug("setVolume " + volume);
|
|
864
|
+
}
|
|
683
865
|
|
|
684
866
|
if (audioAssetList.containsKey(audioId)) {
|
|
685
867
|
AudioAsset asset = audioAssetList.get(audioId);
|
|
686
868
|
if (asset != null) {
|
|
687
|
-
|
|
869
|
+
double durationMs = durationSecs * 1000;
|
|
870
|
+
asset.setVolume(volume, durationMs);
|
|
688
871
|
call.resolve();
|
|
689
872
|
} else {
|
|
690
873
|
call.reject(ERROR_AUDIO_ASSET_MISSING);
|
|
@@ -748,8 +931,12 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
748
931
|
|
|
749
932
|
@PluginMethod
|
|
750
933
|
public void clearCache(PluginCall call) {
|
|
751
|
-
|
|
752
|
-
|
|
934
|
+
try {
|
|
935
|
+
RemoteAudioAsset.clearCache(getContext());
|
|
936
|
+
call.resolve();
|
|
937
|
+
} catch (Exception ex) {
|
|
938
|
+
call.reject(ex.getMessage());
|
|
939
|
+
}
|
|
753
940
|
}
|
|
754
941
|
|
|
755
942
|
@PluginMethod
|
|
@@ -758,7 +945,10 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
758
945
|
initSoundPool();
|
|
759
946
|
|
|
760
947
|
String audioId = call.getString(ASSET_ID);
|
|
761
|
-
|
|
948
|
+
clearFadeOutToStopTimer(audioId);
|
|
949
|
+
double time = call.getDouble(TIME, 0.0);
|
|
950
|
+
|
|
951
|
+
cancelPendingPlay(audioId);
|
|
762
952
|
|
|
763
953
|
if (!isStringValid(audioId)) {
|
|
764
954
|
call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
|
|
@@ -813,6 +1003,23 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
813
1003
|
ret.put("currentTime", roundedTime);
|
|
814
1004
|
ret.put("assetId", assetId);
|
|
815
1005
|
notifyListeners("currentTime", ret);
|
|
1006
|
+
|
|
1007
|
+
JSObject data = getAudioAssetData(assetId);
|
|
1008
|
+
if (data.optBoolean("fadeOut", false)) {
|
|
1009
|
+
double fadeOutStartTime = data.optDouble("fadeOutStartTime", -1);
|
|
1010
|
+
if (fadeOutStartTime >= 0 && currentTime >= fadeOutStartTime) {
|
|
1011
|
+
double fadeOutDuration = data.optDouble("fadeOutDuration", AudioAsset.DEFAULT_FADE_DURATION_MS);
|
|
1012
|
+
try {
|
|
1013
|
+
AudioAsset asset = audioAssetList.get(assetId);
|
|
1014
|
+
if (asset != null) {
|
|
1015
|
+
asset.stopWithFade(fadeOutDuration, false);
|
|
1016
|
+
}
|
|
1017
|
+
} catch (Exception e) {
|
|
1018
|
+
logger.error("Error triggering scheduled fade-out", e);
|
|
1019
|
+
}
|
|
1020
|
+
clearFadeOutToStopTimer(assetId);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
816
1023
|
}
|
|
817
1024
|
|
|
818
1025
|
/**
|
|
@@ -966,7 +1173,10 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
966
1173
|
boolean isLocalUrl = call.getBoolean("isUrl", false);
|
|
967
1174
|
boolean isComplex = call.getBoolean("isComplex", false);
|
|
968
1175
|
|
|
969
|
-
Log.d(
|
|
1176
|
+
Log.d(
|
|
1177
|
+
TAG,
|
|
1178
|
+
"Preloading asset: " + audioId + ", path: " + assetPath + ", isLocalUrl: " + isLocalUrl + ", isComplex: " + isComplex
|
|
1179
|
+
);
|
|
970
1180
|
|
|
971
1181
|
if (audioAssetList.containsKey(audioId)) {
|
|
972
1182
|
call.reject(ERROR_AUDIO_EXISTS + " - " + audioId);
|
|
@@ -1024,11 +1234,7 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
1024
1234
|
if (LOOP.equals(action)) {
|
|
1025
1235
|
asset.loop();
|
|
1026
1236
|
} else {
|
|
1027
|
-
|
|
1028
|
-
asset.playWithFade(time);
|
|
1029
|
-
} else {
|
|
1030
|
-
asset.play(time);
|
|
1031
|
-
}
|
|
1237
|
+
asset.play(time);
|
|
1032
1238
|
}
|
|
1033
1239
|
|
|
1034
1240
|
// Update notification if enabled
|
|
@@ -1045,18 +1251,79 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
1045
1251
|
call.reject("Asset not found: " + audioId);
|
|
1046
1252
|
}
|
|
1047
1253
|
} catch (Exception ex) {
|
|
1048
|
-
|
|
1254
|
+
logger.error("Error in playOrLoop", ex);
|
|
1049
1255
|
call.reject(ex.getMessage());
|
|
1050
1256
|
}
|
|
1051
1257
|
}
|
|
1052
1258
|
|
|
1259
|
+
private void scheduleFadeOut(AudioAsset asset, double fadeOutDurationMs, double fadeOutStartTimeMs) {
|
|
1260
|
+
try {
|
|
1261
|
+
double duration = asset.getDuration();
|
|
1262
|
+
if (duration > 0) {
|
|
1263
|
+
double fadeOutStartTime = duration - (fadeOutDurationMs / 1000.0);
|
|
1264
|
+
if (fadeOutStartTimeMs > 0) {
|
|
1265
|
+
fadeOutStartTime = fadeOutStartTimeMs / 1000.0;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
logger.debug("Scheduling fade-out for asset: " + asset.assetId + ", start time: " + fadeOutStartTime + " seconds");
|
|
1269
|
+
|
|
1270
|
+
// Store fade-out parameters in asset data
|
|
1271
|
+
JSObject data = getAudioAssetData(asset.assetId);
|
|
1272
|
+
data.put("fadeOut", true);
|
|
1273
|
+
data.put("fadeOutStartTime", fadeOutStartTime);
|
|
1274
|
+
data.put("fadeOutDuration", fadeOutDurationMs);
|
|
1275
|
+
setAudioAssetData(asset.assetId, data);
|
|
1276
|
+
} else {
|
|
1277
|
+
logger.warning("Duration not available, skipping fade-out scheduling");
|
|
1278
|
+
}
|
|
1279
|
+
} catch (Exception e) {
|
|
1280
|
+
logger.error("Error handling fade-out", e);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
private void handleFadeOut(AudioAsset asset, String audioId, double fadeOutDurationMs, double fadeOutStartTimeSecs) {
|
|
1285
|
+
try {
|
|
1286
|
+
double duration = asset.getDuration();
|
|
1287
|
+
if (duration <= 0) {
|
|
1288
|
+
logger.warning("Duration not available, skipping fade-out scheduling");
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
double fadeOutStartTime = duration - (fadeOutDurationMs / 1000.0);
|
|
1293
|
+
if (fadeOutStartTimeSecs > 0) {
|
|
1294
|
+
fadeOutStartTime = fadeOutStartTimeSecs;
|
|
1295
|
+
}
|
|
1296
|
+
fadeOutStartTime = Math.max(fadeOutStartTime, 0);
|
|
1297
|
+
|
|
1298
|
+
JSObject data = getAudioAssetData(audioId);
|
|
1299
|
+
data.put("fadeOut", true);
|
|
1300
|
+
data.put("fadeOutStartTime", fadeOutStartTime);
|
|
1301
|
+
data.put("fadeOutDuration", fadeOutDurationMs);
|
|
1302
|
+
setAudioAssetData(audioId, data);
|
|
1303
|
+
} catch (Exception e) {
|
|
1304
|
+
logger.error("Error scheduling fade-out", e);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
private void clearFadeOutToStopTimer(String audioId) {
|
|
1309
|
+
JSObject data = getAudioAssetData(audioId);
|
|
1310
|
+
if (data.has("fadeOut")) {
|
|
1311
|
+
logger.debug("Cancelling fade-out for asset: " + audioId);
|
|
1312
|
+
data.remove("fadeOut");
|
|
1313
|
+
data.remove("fadeOutStartTime");
|
|
1314
|
+
data.remove("fadeOutDuration");
|
|
1315
|
+
setAudioAssetData(audioId, data);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1053
1319
|
private void initSoundPool() {
|
|
1054
1320
|
if (audioAssetList == null) {
|
|
1055
|
-
|
|
1321
|
+
logger.debug("Initializing audio asset list");
|
|
1322
|
+
audioAssetList = new ConcurrentHashMap<>();
|
|
1056
1323
|
}
|
|
1057
|
-
|
|
1058
1324
|
if (resumeList == null) {
|
|
1059
|
-
|
|
1325
|
+
logger.debug("Initializing resume list");
|
|
1326
|
+
resumeList = new CopyOnWriteArrayList<>();
|
|
1060
1327
|
}
|
|
1061
1328
|
}
|
|
1062
1329
|
|
|
@@ -1121,15 +1388,15 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
1121
1388
|
}
|
|
1122
1389
|
}
|
|
1123
1390
|
|
|
1124
|
-
private void stopAudio(String audioId) throws Exception {
|
|
1391
|
+
private void stopAudio(String audioId, boolean fadeOut, double fadeOutDurationMs) throws Exception {
|
|
1125
1392
|
if (!audioAssetList.containsKey(audioId)) {
|
|
1126
1393
|
throw new Exception(ERROR_ASSET_NOT_LOADED);
|
|
1127
1394
|
}
|
|
1128
1395
|
|
|
1129
1396
|
AudioAsset asset = audioAssetList.get(audioId);
|
|
1130
1397
|
if (asset != null) {
|
|
1131
|
-
if (
|
|
1132
|
-
asset.stopWithFade();
|
|
1398
|
+
if (fadeOut) {
|
|
1399
|
+
asset.stopWithFade(fadeOutDurationMs, false);
|
|
1133
1400
|
} else {
|
|
1134
1401
|
asset.stop();
|
|
1135
1402
|
}
|
|
@@ -1137,12 +1404,12 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
1137
1404
|
}
|
|
1138
1405
|
|
|
1139
1406
|
private void saveDurationCall(String audioId, PluginCall call) {
|
|
1140
|
-
|
|
1407
|
+
logger.debug("Saving duration call for later: " + audioId);
|
|
1141
1408
|
pendingDurationCalls.put(audioId, call);
|
|
1142
1409
|
}
|
|
1143
1410
|
|
|
1144
1411
|
public void notifyDurationAvailable(String assetId, double duration) {
|
|
1145
|
-
|
|
1412
|
+
logger.debug("Duration available for " + assetId + ": " + duration);
|
|
1146
1413
|
PluginCall savedCall = pendingDurationCalls.remove(assetId);
|
|
1147
1414
|
if (savedCall != null) {
|
|
1148
1415
|
JSObject ret = new JSObject();
|
|
@@ -1151,6 +1418,18 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
1151
1418
|
}
|
|
1152
1419
|
}
|
|
1153
1420
|
|
|
1421
|
+
private JSObject getAudioAssetData(String audioId) {
|
|
1422
|
+
JSObject data = audioData.get(audioId);
|
|
1423
|
+
if (data == null) {
|
|
1424
|
+
data = new JSObject();
|
|
1425
|
+
}
|
|
1426
|
+
return data;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
private void setAudioAssetData(String audioId, JSObject data) {
|
|
1430
|
+
audioData.put(audioId, data);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1154
1433
|
@PluginMethod
|
|
1155
1434
|
public void getPluginVersion(final PluginCall call) {
|
|
1156
1435
|
try {
|
|
@@ -1253,7 +1532,7 @@ public class NativeAudio extends Plugin implements AudioManager.OnAudioFocusChan
|
|
|
1253
1532
|
public void onStop() {
|
|
1254
1533
|
if (currentlyPlayingAssetId != null) {
|
|
1255
1534
|
try {
|
|
1256
|
-
stopAudio(currentlyPlayingAssetId);
|
|
1535
|
+
stopAudio(currentlyPlayingAssetId, false, 0);
|
|
1257
1536
|
clearNotification();
|
|
1258
1537
|
currentlyPlayingAssetId = null;
|
|
1259
1538
|
} catch (Exception e) {
|