@capgo/capacitor-audio-recorder 7.0.0
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/CapgoCapacitorAudioRecorder.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +359 -0
- package/android/build.gradle +57 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/app/capgo/audiorecorder/CapacitorAudioRecorderPlugin.java +285 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/docs.json +646 -0
- package/dist/esm/definitions.d.ts +231 -0
- package/dist/esm/definitions.js +43 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +34 -0
- package/dist/esm/web.js +242 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +298 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +301 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapacitorAudioRecorderPlugin/CapacitorAudioRecorderPlugin.swift +321 -0
- package/ios/Tests/CapacitorAudioRecorderPluginTests/CapacitorAudioRecorderPluginTests.swift +9 -0
- package/package.json +89 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
package app.capgo.audiorecorder;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.media.MediaRecorder;
|
|
5
|
+
import android.net.Uri;
|
|
6
|
+
import android.os.Build;
|
|
7
|
+
import android.os.SystemClock;
|
|
8
|
+
import androidx.annotation.Nullable;
|
|
9
|
+
import com.getcapacitor.JSObject;
|
|
10
|
+
import com.getcapacitor.PermissionState;
|
|
11
|
+
import com.getcapacitor.PluginCall;
|
|
12
|
+
import com.getcapacitor.PluginMethod;
|
|
13
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
14
|
+
import com.getcapacitor.annotation.Permission;
|
|
15
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
16
|
+
import java.io.File;
|
|
17
|
+
import java.io.IOException;
|
|
18
|
+
import java.text.SimpleDateFormat;
|
|
19
|
+
import java.util.Date;
|
|
20
|
+
import java.util.Locale;
|
|
21
|
+
|
|
22
|
+
@CapacitorPlugin(
|
|
23
|
+
name = "CapacitorAudioRecorder",
|
|
24
|
+
permissions = { @Permission(alias = "microphone", strings = { Manifest.permission.RECORD_AUDIO }) }
|
|
25
|
+
)
|
|
26
|
+
public class CapacitorAudioRecorderPlugin extends com.getcapacitor.Plugin {
|
|
27
|
+
|
|
28
|
+
private enum RecordingStatus {
|
|
29
|
+
INACTIVE,
|
|
30
|
+
RECORDING,
|
|
31
|
+
PAUSED
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private MediaRecorder mediaRecorder;
|
|
35
|
+
private File outputFile;
|
|
36
|
+
private RecordingStatus status = RecordingStatus.INACTIVE;
|
|
37
|
+
private long recordingStartTime = 0L;
|
|
38
|
+
private long pauseStartTime = 0L;
|
|
39
|
+
private long accumulatedPauseDuration = 0L;
|
|
40
|
+
|
|
41
|
+
@PluginMethod
|
|
42
|
+
public void startRecording(PluginCall call) {
|
|
43
|
+
if (status != RecordingStatus.INACTIVE) {
|
|
44
|
+
call.reject("Recording already in progress.");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!ensurePermission(call)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
int bitRate = call.getInt("bitRate", 192000);
|
|
53
|
+
int sampleRate = call.getInt("sampleRate", 44100);
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
prepareRecorder(bitRate, sampleRate);
|
|
57
|
+
mediaRecorder.start();
|
|
58
|
+
recordingStartTime = SystemClock.elapsedRealtime();
|
|
59
|
+
accumulatedPauseDuration = 0;
|
|
60
|
+
pauseStartTime = 0;
|
|
61
|
+
status = RecordingStatus.RECORDING;
|
|
62
|
+
call.resolve();
|
|
63
|
+
} catch (IOException ex) {
|
|
64
|
+
releaseRecorder();
|
|
65
|
+
call.reject("Unable to start recording.", ex);
|
|
66
|
+
} catch (IllegalStateException ex) {
|
|
67
|
+
releaseRecorder();
|
|
68
|
+
call.reject("Recording failed to start.", ex);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@PluginMethod
|
|
73
|
+
public void pauseRecording(PluginCall call) {
|
|
74
|
+
if (status != RecordingStatus.RECORDING || mediaRecorder == null) {
|
|
75
|
+
call.reject("No active recording to pause.");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
80
|
+
call.reject("Pausing is not supported on this Android version.");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
mediaRecorder.pause();
|
|
86
|
+
pauseStartTime = SystemClock.elapsedRealtime();
|
|
87
|
+
status = RecordingStatus.PAUSED;
|
|
88
|
+
notifyListeners("recordingPaused", new JSObject());
|
|
89
|
+
call.resolve();
|
|
90
|
+
} catch (IllegalStateException ex) {
|
|
91
|
+
call.reject("Failed to pause recording.", ex);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@PluginMethod
|
|
96
|
+
public void resumeRecording(PluginCall call) {
|
|
97
|
+
if (status != RecordingStatus.PAUSED || mediaRecorder == null) {
|
|
98
|
+
call.reject("No paused recording to resume.");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
103
|
+
call.reject("Resuming is not supported on this Android version.");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
mediaRecorder.resume();
|
|
109
|
+
if (pauseStartTime > 0) {
|
|
110
|
+
accumulatedPauseDuration += SystemClock.elapsedRealtime() - pauseStartTime;
|
|
111
|
+
}
|
|
112
|
+
pauseStartTime = 0;
|
|
113
|
+
status = RecordingStatus.RECORDING;
|
|
114
|
+
call.resolve();
|
|
115
|
+
} catch (IllegalStateException ex) {
|
|
116
|
+
call.reject("Failed to resume recording.", ex);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@PluginMethod
|
|
121
|
+
public void stopRecording(PluginCall call) {
|
|
122
|
+
if (status == RecordingStatus.INACTIVE || mediaRecorder == null) {
|
|
123
|
+
call.reject("No active recording to stop.");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
mediaRecorder.stop();
|
|
129
|
+
} catch (RuntimeException ex) {
|
|
130
|
+
deleteOutputFile();
|
|
131
|
+
releaseRecorder();
|
|
132
|
+
call.reject("Failed to stop recording cleanly.", ex);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
long duration = calculateDuration();
|
|
137
|
+
String uri = outputFile != null ? Uri.fromFile(outputFile).toString() : "";
|
|
138
|
+
|
|
139
|
+
JSObject result = new JSObject();
|
|
140
|
+
result.put("duration", duration);
|
|
141
|
+
result.put("uri", uri);
|
|
142
|
+
|
|
143
|
+
notifyListeners("recordingStopped", result);
|
|
144
|
+
releaseRecorder();
|
|
145
|
+
call.resolve(result);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@PluginMethod
|
|
149
|
+
public void cancelRecording(PluginCall call) {
|
|
150
|
+
if (mediaRecorder != null) {
|
|
151
|
+
try {
|
|
152
|
+
mediaRecorder.stop();
|
|
153
|
+
} catch (RuntimeException ignored) {}
|
|
154
|
+
}
|
|
155
|
+
deleteOutputFile();
|
|
156
|
+
releaseRecorder();
|
|
157
|
+
call.resolve();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@PluginMethod
|
|
161
|
+
public void getRecordingStatus(PluginCall call) {
|
|
162
|
+
JSObject result = new JSObject();
|
|
163
|
+
result.put("status", status.name());
|
|
164
|
+
call.resolve(result);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@PluginMethod
|
|
168
|
+
public void checkPermissions(PluginCall call) {
|
|
169
|
+
PermissionState state = getPermissionState("microphone");
|
|
170
|
+
JSObject result = new JSObject();
|
|
171
|
+
result.put("recordAudio", toPermissionString(state));
|
|
172
|
+
call.resolve(result);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@PluginMethod
|
|
176
|
+
public void requestPermissions(PluginCall call) {
|
|
177
|
+
if (getPermissionState("microphone") == PermissionState.GRANTED) {
|
|
178
|
+
JSObject result = new JSObject();
|
|
179
|
+
result.put("recordAudio", "granted");
|
|
180
|
+
call.resolve(result);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
requestPermissionForAlias("microphone", call, "microphonePermissionCallback");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@PermissionCallback
|
|
187
|
+
public void microphonePermissionCallback(PluginCall call) {
|
|
188
|
+
PermissionState state = getPermissionState("microphone");
|
|
189
|
+
JSObject result = new JSObject();
|
|
190
|
+
result.put("recordAudio", toPermissionString(state));
|
|
191
|
+
call.resolve(result);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@PluginMethod
|
|
195
|
+
public void removeAllListeners(PluginCall call) {
|
|
196
|
+
super.removeAllListeners(call);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@Override
|
|
200
|
+
protected void handleOnDestroy() {
|
|
201
|
+
super.handleOnDestroy();
|
|
202
|
+
releaseRecorder();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private boolean ensurePermission(PluginCall call) {
|
|
206
|
+
PermissionState state = getPermissionState("microphone");
|
|
207
|
+
if (state == PermissionState.GRANTED) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
requestPermissionForAlias("microphone", call, "microphonePermissionStartCallback");
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@PermissionCallback
|
|
215
|
+
public void microphonePermissionStartCallback(PluginCall call) {
|
|
216
|
+
if (getPermissionState("microphone") != PermissionState.GRANTED) {
|
|
217
|
+
call.reject("Microphone permission not granted.");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
startRecording(call);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private void prepareRecorder(int bitRate, int sampleRate) throws IOException {
|
|
224
|
+
releaseRecorder();
|
|
225
|
+
mediaRecorder = new MediaRecorder();
|
|
226
|
+
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
|
227
|
+
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
|
|
228
|
+
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
|
|
229
|
+
mediaRecorder.setAudioEncodingBitRate(bitRate);
|
|
230
|
+
mediaRecorder.setAudioSamplingRate(sampleRate);
|
|
231
|
+
|
|
232
|
+
File outputDirectory = new File(getContext().getCacheDir(), "capacitor-audio-recorder");
|
|
233
|
+
if (!outputDirectory.exists()) {
|
|
234
|
+
//noinspection ResultOfMethodCallIgnored
|
|
235
|
+
outputDirectory.mkdirs();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
String fileName = "recording-" + new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US).format(new Date()) + ".m4a";
|
|
239
|
+
outputFile = new File(outputDirectory, fileName);
|
|
240
|
+
mediaRecorder.setOutputFile(outputFile.getAbsolutePath());
|
|
241
|
+
mediaRecorder.prepare();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private void releaseRecorder() {
|
|
245
|
+
if (mediaRecorder != null) {
|
|
246
|
+
mediaRecorder.reset();
|
|
247
|
+
mediaRecorder.release();
|
|
248
|
+
}
|
|
249
|
+
mediaRecorder = null;
|
|
250
|
+
status = RecordingStatus.INACTIVE;
|
|
251
|
+
recordingStartTime = 0;
|
|
252
|
+
pauseStartTime = 0;
|
|
253
|
+
accumulatedPauseDuration = 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private void deleteOutputFile() {
|
|
257
|
+
if (outputFile != null && outputFile.exists()) {
|
|
258
|
+
//noinspection ResultOfMethodCallIgnored
|
|
259
|
+
outputFile.delete();
|
|
260
|
+
}
|
|
261
|
+
outputFile = null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private long calculateDuration() {
|
|
265
|
+
long endTime = SystemClock.elapsedRealtime();
|
|
266
|
+
if (pauseStartTime > 0) {
|
|
267
|
+
accumulatedPauseDuration += endTime - pauseStartTime;
|
|
268
|
+
}
|
|
269
|
+
long duration = endTime - recordingStartTime - accumulatedPauseDuration;
|
|
270
|
+
return Math.max(duration, 0);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private String toPermissionString(PermissionState state) {
|
|
274
|
+
switch (state) {
|
|
275
|
+
case GRANTED:
|
|
276
|
+
return "granted";
|
|
277
|
+
case DENIED:
|
|
278
|
+
return "denied";
|
|
279
|
+
case PROMPT_WITH_RATIONALE:
|
|
280
|
+
return "prompt-with-rationale";
|
|
281
|
+
default:
|
|
282
|
+
return "prompt";
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
File without changes
|