@capgo/native-audio 6.4.5 → 6.4.7
Sign up to get free protection for your applications and to get access to all the features.
@@ -29,7 +29,11 @@ import com.getcapacitor.PluginMethod;
|
|
29
29
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
30
30
|
import com.getcapacitor.annotation.Permission;
|
31
31
|
import java.io.File;
|
32
|
+
import java.io.FileDescriptor;
|
33
|
+
import java.io.FileInputStream;
|
34
|
+
import java.io.IOException;
|
32
35
|
import java.net.URI;
|
36
|
+
import java.net.URL;
|
33
37
|
import java.util.ArrayList;
|
34
38
|
import java.util.HashMap;
|
35
39
|
|
@@ -56,6 +60,8 @@ public class NativeAudio
|
|
56
60
|
|
57
61
|
this.audioManager = (AudioManager) this.getActivity()
|
58
62
|
.getSystemService(Context.AUDIO_SERVICE);
|
63
|
+
|
64
|
+
audioAssetList = new HashMap<>();
|
59
65
|
}
|
60
66
|
|
61
67
|
@Override
|
@@ -448,88 +454,132 @@ public class NativeAudio
|
|
448
454
|
initSoundPool();
|
449
455
|
|
450
456
|
String audioId = call.getString(ASSET_ID);
|
451
|
-
|
452
|
-
boolean isLocalUrl = Boolean.TRUE.equals(call.getBoolean("isUrl", false));
|
453
|
-
|
454
457
|
if (!isStringValid(audioId)) {
|
455
458
|
call.reject(ERROR_AUDIO_ID_MISSING + " - " + audioId);
|
456
459
|
return;
|
457
460
|
}
|
458
461
|
|
459
|
-
|
460
|
-
|
462
|
+
String assetPath = call.getString(ASSET_PATH);
|
463
|
+
if (!isStringValid(assetPath)) {
|
464
|
+
call.reject(
|
465
|
+
ERROR_ASSET_PATH_MISSING + " - " + audioId + " - " + assetPath
|
466
|
+
);
|
467
|
+
return;
|
468
|
+
}
|
461
469
|
|
462
|
-
|
463
|
-
|
464
|
-
ERROR_ASSET_PATH_MISSING + " - " + audioId + " - " + assetPath
|
465
|
-
);
|
466
|
-
return;
|
467
|
-
}
|
470
|
+
boolean isLocalUrl = call.getBoolean("isUrl", false);
|
471
|
+
boolean isComplex = call.getBoolean("isComplex", false);
|
468
472
|
|
469
|
-
|
473
|
+
Log.d(
|
474
|
+
"AudioPlugin",
|
475
|
+
"Debug: audioId = " +
|
476
|
+
audioId +
|
477
|
+
", assetPath = " +
|
478
|
+
assetPath +
|
479
|
+
", isLocalUrl = " +
|
480
|
+
isLocalUrl
|
481
|
+
);
|
470
482
|
|
483
|
+
if (audioAssetList.containsKey(audioId)) {
|
484
|
+
call.reject(ERROR_AUDIO_EXISTS + " - " + audioId);
|
485
|
+
return;
|
486
|
+
}
|
487
|
+
|
488
|
+
if (isComplex) {
|
471
489
|
volume = call.getFloat(VOLUME, 1F);
|
472
490
|
audioChannelNum = call.getInt(AUDIO_CHANNEL_NUM, 1);
|
491
|
+
}
|
473
492
|
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
uri.
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
493
|
+
if (isLocalUrl) {
|
494
|
+
// Handle URL (both remote and local file URLs)
|
495
|
+
Log.d("AudioPlugin", "Debug: Handling URL");
|
496
|
+
try {
|
497
|
+
Uri uri = Uri.parse(assetPath);
|
498
|
+
if (
|
499
|
+
uri.getScheme() != null &&
|
500
|
+
(uri.getScheme().equals("http") || uri.getScheme().equals("https"))
|
501
|
+
) {
|
502
|
+
// Remote URL
|
503
|
+
Log.d(
|
504
|
+
"AudioPlugin",
|
505
|
+
"Debug: Remote URL detected: " + uri.toString()
|
506
|
+
);
|
507
|
+
RemoteAudioAsset remoteAudioAsset = new RemoteAudioAsset(
|
508
|
+
this,
|
509
|
+
audioId,
|
510
|
+
uri,
|
511
|
+
audioChannelNum,
|
512
|
+
volume
|
513
|
+
);
|
514
|
+
audioAssetList.put(audioId, remoteAudioAsset);
|
515
|
+
} else if (
|
516
|
+
uri.getScheme() != null && uri.getScheme().equals("file")
|
517
|
+
) {
|
518
|
+
// Local file URL
|
519
|
+
Log.d("AudioPlugin", "Debug: Local file URL detected");
|
520
|
+
File file = new File(uri.getPath());
|
521
|
+
if (!file.exists()) {
|
522
|
+
Log.e(
|
523
|
+
"AudioPlugin",
|
524
|
+
"Error: File does not exist - " + file.getAbsolutePath()
|
497
525
|
);
|
498
|
-
|
499
|
-
call.resolve(status);
|
526
|
+
call.reject(ERROR_ASSET_PATH_MISSING + " - " + assetPath);
|
500
527
|
return;
|
501
|
-
} else {
|
502
|
-
// It's a local file path
|
503
|
-
// Check if fullPath starts with "public/" and prepend if necessary
|
504
|
-
if (!fullPath.startsWith("public/")) {
|
505
|
-
fullPath = "public/".concat(fullPath);
|
506
|
-
}
|
507
|
-
Context ctx = getContext().getApplicationContext(); // Use getContext() directly
|
508
|
-
AssetManager am = ctx.getResources().getAssets();
|
509
|
-
// Remove the redefinition of assetFileDescriptor
|
510
|
-
assetFileDescriptor = am.openFd(fullPath);
|
511
528
|
}
|
512
|
-
|
513
|
-
|
514
|
-
|
529
|
+
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
|
530
|
+
file,
|
531
|
+
ParcelFileDescriptor.MODE_READ_ONLY
|
532
|
+
);
|
533
|
+
AssetFileDescriptor afd = new AssetFileDescriptor(
|
534
|
+
pfd,
|
535
|
+
0,
|
536
|
+
AssetFileDescriptor.UNKNOWN_LENGTH
|
537
|
+
);
|
538
|
+
AudioAsset asset = new AudioAsset(
|
539
|
+
this,
|
540
|
+
audioId,
|
541
|
+
afd,
|
542
|
+
audioChannelNum,
|
543
|
+
volume
|
544
|
+
);
|
545
|
+
audioAssetList.put(audioId, asset);
|
546
|
+
} else {
|
547
|
+
throw new IllegalArgumentException(
|
548
|
+
"Invalid URL scheme: " + uri.getScheme()
|
549
|
+
);
|
515
550
|
}
|
551
|
+
call.resolve(status);
|
552
|
+
} catch (Exception e) {
|
553
|
+
Log.e("AudioPlugin", "Error handling URL", e);
|
554
|
+
call.reject("Error handling URL: " + e.getMessage());
|
516
555
|
}
|
517
|
-
|
518
|
-
AudioAsset asset = new AudioAsset(
|
519
|
-
this,
|
520
|
-
audioId,
|
521
|
-
assetFileDescriptor,
|
522
|
-
audioChannelNum,
|
523
|
-
volume
|
524
|
-
);
|
525
|
-
audioAssetList.put(audioId, asset);
|
526
|
-
|
527
|
-
call.resolve(status);
|
528
556
|
} else {
|
529
|
-
|
557
|
+
// Handle asset in public folder
|
558
|
+
Log.d("AudioPlugin", "Debug: Handling asset in public folder");
|
559
|
+
if (!assetPath.startsWith("public/")) {
|
560
|
+
assetPath = "public/" + assetPath;
|
561
|
+
}
|
562
|
+
try {
|
563
|
+
Context ctx = getContext().getApplicationContext();
|
564
|
+
AssetManager am = ctx.getResources().getAssets();
|
565
|
+
AssetFileDescriptor assetFileDescriptor = am.openFd(assetPath);
|
566
|
+
AudioAsset asset = new AudioAsset(
|
567
|
+
this,
|
568
|
+
audioId,
|
569
|
+
assetFileDescriptor,
|
570
|
+
audioChannelNum,
|
571
|
+
volume
|
572
|
+
);
|
573
|
+
audioAssetList.put(audioId, asset);
|
574
|
+
call.resolve(status);
|
575
|
+
} catch (IOException e) {
|
576
|
+
Log.e("AudioPlugin", "Error opening asset: " + assetPath, e);
|
577
|
+
call.reject(ERROR_ASSET_PATH_MISSING + " - " + assetPath);
|
578
|
+
}
|
530
579
|
}
|
531
580
|
} catch (Exception ex) {
|
532
|
-
|
581
|
+
Log.e("AudioPlugin", "Error in preloadAsset", ex);
|
582
|
+
call.reject("Error in preloadAsset: " + ex.getMessage());
|
533
583
|
}
|
534
584
|
}
|
535
585
|
|
@@ -2,10 +2,17 @@ package ee.forgr.audio;
|
|
2
2
|
|
3
3
|
import android.media.MediaPlayer;
|
4
4
|
import android.net.Uri;
|
5
|
+
import android.util.Log;
|
6
|
+
import java.util.ArrayList;
|
5
7
|
|
6
8
|
public class RemoteAudioAsset extends AudioAsset {
|
7
9
|
|
8
|
-
private
|
10
|
+
private static final String TAG = "RemoteAudioAsset";
|
11
|
+
private final ArrayList<MediaPlayer> mediaPlayers;
|
12
|
+
private int playIndex = 0;
|
13
|
+
private final Uri uri;
|
14
|
+
private float volume;
|
15
|
+
private boolean isPrepared = false;
|
9
16
|
|
10
17
|
public RemoteAudioAsset(
|
11
18
|
NativeAudio owner,
|
@@ -14,74 +21,155 @@ public class RemoteAudioAsset extends AudioAsset {
|
|
14
21
|
int audioChannelNum,
|
15
22
|
float volume
|
16
23
|
) throws Exception {
|
17
|
-
super(owner, assetId, null,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
super(owner, assetId, null, 0, volume);
|
25
|
+
this.uri = uri;
|
26
|
+
this.volume = volume;
|
27
|
+
this.mediaPlayers = new ArrayList<>();
|
28
|
+
|
29
|
+
if (audioChannelNum < 1) {
|
30
|
+
audioChannelNum = 1;
|
31
|
+
}
|
32
|
+
|
33
|
+
for (int i = 0; i < audioChannelNum; i++) {
|
34
|
+
MediaPlayer mediaPlayer = new MediaPlayer();
|
35
|
+
mediaPlayers.add(mediaPlayer);
|
36
|
+
initializeMediaPlayer(mediaPlayer);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
private void initializeMediaPlayer(MediaPlayer mediaPlayer) {
|
41
|
+
try {
|
42
|
+
mediaPlayer.setDataSource(owner.getContext(), uri);
|
43
|
+
mediaPlayer.setVolume(volume, volume);
|
44
|
+
mediaPlayer.setOnPreparedListener(mp -> {
|
45
|
+
isPrepared = true;
|
46
|
+
Log.d(TAG, "MediaPlayer prepared for " + uri.toString());
|
47
|
+
});
|
48
|
+
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
|
49
|
+
Log.e(TAG, "MediaPlayer error: " + what + ", " + extra);
|
50
|
+
return false;
|
51
|
+
});
|
52
|
+
mediaPlayer.prepareAsync();
|
53
|
+
} catch (Exception e) {
|
54
|
+
Log.e(TAG, "Error initializing MediaPlayer", e);
|
55
|
+
}
|
22
56
|
}
|
23
57
|
|
24
58
|
@Override
|
25
59
|
public void play(Double time) throws Exception {
|
26
|
-
if (
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
60
|
+
if (mediaPlayers.isEmpty()) {
|
61
|
+
throw new Exception("No MediaPlayer available");
|
62
|
+
}
|
63
|
+
|
64
|
+
MediaPlayer mediaPlayer = mediaPlayers.get(playIndex);
|
65
|
+
if (!isPrepared) {
|
66
|
+
Log.d(TAG, "MediaPlayer not yet prepared, waiting...");
|
67
|
+
mediaPlayer.setOnPreparedListener(mp -> {
|
68
|
+
isPrepared = true;
|
69
|
+
try {
|
70
|
+
playInternal(mediaPlayer, time);
|
71
|
+
} catch (Exception e) {
|
72
|
+
Log.e(TAG, "Error playing after prepare", e);
|
73
|
+
}
|
74
|
+
});
|
31
75
|
} else {
|
32
|
-
|
76
|
+
playInternal(mediaPlayer, time);
|
77
|
+
}
|
78
|
+
|
79
|
+
playIndex = (playIndex + 1) % mediaPlayers.size();
|
80
|
+
}
|
81
|
+
|
82
|
+
private void playInternal(MediaPlayer mediaPlayer, Double time)
|
83
|
+
throws Exception {
|
84
|
+
if (time != null) {
|
85
|
+
mediaPlayer.seekTo((int) (time * 1000));
|
33
86
|
}
|
87
|
+
mediaPlayer.start();
|
34
88
|
}
|
35
89
|
|
36
90
|
@Override
|
37
91
|
public boolean pause() throws Exception {
|
38
|
-
|
39
|
-
|
40
|
-
|
92
|
+
boolean wasPlaying = false;
|
93
|
+
for (MediaPlayer mediaPlayer : mediaPlayers) {
|
94
|
+
if (mediaPlayer.isPlaying()) {
|
95
|
+
mediaPlayer.pause();
|
96
|
+
wasPlaying = true;
|
97
|
+
}
|
41
98
|
}
|
42
|
-
return
|
99
|
+
return wasPlaying;
|
43
100
|
}
|
44
101
|
|
45
102
|
@Override
|
46
103
|
public void resume() throws Exception {
|
47
|
-
|
48
|
-
mediaPlayer.
|
104
|
+
for (MediaPlayer mediaPlayer : mediaPlayers) {
|
105
|
+
if (!mediaPlayer.isPlaying()) {
|
106
|
+
mediaPlayer.start();
|
107
|
+
}
|
49
108
|
}
|
50
109
|
}
|
51
110
|
|
52
111
|
@Override
|
53
112
|
public void stop() throws Exception {
|
54
|
-
|
55
|
-
mediaPlayer.
|
56
|
-
|
113
|
+
for (MediaPlayer mediaPlayer : mediaPlayers) {
|
114
|
+
if (mediaPlayer.isPlaying()) {
|
115
|
+
mediaPlayer.stop();
|
116
|
+
}
|
117
|
+
// Reset the MediaPlayer to make it ready for future playback
|
118
|
+
mediaPlayer.reset();
|
119
|
+
initializeMediaPlayer(mediaPlayer);
|
57
120
|
}
|
121
|
+
isPrepared = false;
|
58
122
|
}
|
59
123
|
|
60
124
|
@Override
|
61
125
|
public void loop() throws Exception {
|
62
|
-
if (
|
126
|
+
if (!mediaPlayers.isEmpty()) {
|
127
|
+
MediaPlayer mediaPlayer = mediaPlayers.get(playIndex);
|
63
128
|
mediaPlayer.setLooping(true);
|
64
129
|
mediaPlayer.start();
|
130
|
+
playIndex = (playIndex + 1) % mediaPlayers.size();
|
65
131
|
}
|
66
132
|
}
|
67
133
|
|
68
134
|
@Override
|
69
135
|
public void unload() throws Exception {
|
70
|
-
|
136
|
+
for (MediaPlayer mediaPlayer : mediaPlayers) {
|
71
137
|
mediaPlayer.release();
|
72
|
-
mediaPlayer = null;
|
73
138
|
}
|
139
|
+
mediaPlayers.clear();
|
74
140
|
}
|
75
141
|
|
76
142
|
@Override
|
77
143
|
public void setVolume(float volume) throws Exception {
|
78
|
-
|
144
|
+
this.volume = volume;
|
145
|
+
for (MediaPlayer mediaPlayer : mediaPlayers) {
|
79
146
|
mediaPlayer.setVolume(volume, volume);
|
80
147
|
}
|
81
148
|
}
|
82
149
|
|
83
150
|
@Override
|
84
151
|
public boolean isPlaying() throws Exception {
|
85
|
-
|
152
|
+
for (MediaPlayer mediaPlayer : mediaPlayers) {
|
153
|
+
if (mediaPlayer.isPlaying()) {
|
154
|
+
return true;
|
155
|
+
}
|
156
|
+
}
|
157
|
+
return false;
|
158
|
+
}
|
159
|
+
|
160
|
+
@Override
|
161
|
+
public double getDuration() {
|
162
|
+
if (!mediaPlayers.isEmpty() && isPrepared) {
|
163
|
+
return mediaPlayers.get(0).getDuration() / 1000.0;
|
164
|
+
}
|
165
|
+
return 0;
|
166
|
+
}
|
167
|
+
|
168
|
+
@Override
|
169
|
+
public double getCurrentPosition() {
|
170
|
+
if (!mediaPlayers.isEmpty() && isPrepared) {
|
171
|
+
return mediaPlayers.get(0).getCurrentPosition() / 1000.0;
|
172
|
+
}
|
173
|
+
return 0;
|
86
174
|
}
|
87
175
|
}
|