@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.
@@ -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