@independo/capacitor-voice-recorder 8.1.0-dev.1 → 8.1.0-dev.2
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 +40 -30
- package/android/build.gradle +44 -1
- package/android/src/main/java/app/independo/capacitorvoicerecorder/VoiceRecorder.java +146 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/PermissionChecker.java +8 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecordDataMapper.java +32 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderAdapter.java +39 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderPlatform.java +25 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/CurrentRecordingStatus.java +9 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ErrorCodes.java +19 -0
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/Messages.java +2 -1
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/RecordData.java +15 -1
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/RecordOptions.java +4 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ResponseFormat.java +18 -0
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/ResponseGenerator.java +7 -1
- package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/platform}/CustomMediaRecorder.java +33 -2
- package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/DefaultRecorderPlatform.java +86 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/NotSupportedOsVersion.java +4 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderService.java +144 -0
- package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderServiceException.java +23 -0
- package/dist/esm/adapters/VoiceRecorderWebAdapter.d.ts +23 -0
- package/dist/esm/adapters/VoiceRecorderWebAdapter.js +41 -0
- package/dist/esm/adapters/VoiceRecorderWebAdapter.js.map +1 -0
- package/dist/esm/core/error-codes.d.ts +4 -0
- package/dist/esm/core/error-codes.js +21 -0
- package/dist/esm/core/error-codes.js.map +1 -0
- package/dist/esm/core/recording-contract.d.ts +3 -0
- package/dist/esm/core/recording-contract.js +15 -0
- package/dist/esm/core/recording-contract.js.map +1 -0
- package/dist/esm/core/response-format.d.ts +8 -0
- package/dist/esm/core/response-format.js +17 -0
- package/dist/esm/core/response-format.js.map +1 -0
- package/dist/esm/platform/web/VoiceRecorderImpl.d.ts +45 -0
- package/dist/esm/{VoiceRecorderImpl.js → platform/web/VoiceRecorderImpl.js} +20 -2
- package/dist/esm/platform/web/VoiceRecorderImpl.js.map +1 -0
- package/dist/esm/platform/web/get-blob-duration.js.map +1 -0
- package/dist/esm/{predefined-web-responses.d.ts → platform/web/predefined-web-responses.d.ts} +12 -1
- package/dist/esm/{predefined-web-responses.js → platform/web/predefined-web-responses.js} +11 -0
- package/dist/esm/platform/web/predefined-web-responses.js.map +1 -0
- package/dist/esm/service/VoiceRecorderService.d.ts +47 -0
- package/dist/esm/service/VoiceRecorderService.js +60 -0
- package/dist/esm/service/VoiceRecorderService.js.map +1 -0
- package/dist/esm/web.d.ts +12 -1
- package/dist/esm/web.js +26 -12
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +200 -9
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +200 -9
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/VoiceRecorder/Adapters/DefaultRecorderPlatform.swift +33 -0
- package/ios/Sources/VoiceRecorder/Adapters/RecordDataMapper.swift +38 -0
- package/ios/Sources/VoiceRecorder/Adapters/RecorderAdapter.swift +24 -0
- package/ios/Sources/VoiceRecorder/Adapters/RecorderPlatform.swift +11 -0
- package/ios/Sources/VoiceRecorder/Bridge/VoiceRecorder.swift +172 -0
- package/ios/Sources/VoiceRecorder/{CurrentRecordingStatus.swift → Core/CurrentRecordingStatus.swift} +1 -0
- package/ios/Sources/VoiceRecorder/Core/ErrorCodes.swift +16 -0
- package/ios/Sources/VoiceRecorder/{Messages.swift → Core/Messages.swift} +1 -0
- package/ios/Sources/VoiceRecorder/{RecordData.swift → Core/RecordData.swift} +6 -0
- package/ios/Sources/VoiceRecorder/Core/RecordOptions.swift +11 -0
- package/ios/Sources/VoiceRecorder/Core/ResponseFormat.swift +22 -0
- package/ios/Sources/VoiceRecorder/{ResponseGenerator.swift → Core/ResponseGenerator.swift} +6 -0
- package/ios/Sources/VoiceRecorder/{CustomMediaRecorder.swift → Platform/CustomMediaRecorder.swift} +25 -1
- package/ios/Sources/VoiceRecorder/Service/VoiceRecorderService.swift +128 -0
- package/ios/Sources/VoiceRecorder/Service/VoiceRecorderServiceError.swift +14 -0
- package/package.json +10 -4
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CurrentRecordingStatus.java +0 -8
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/NotSupportedOsVersion.java +0 -3
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/RecordOptions.java +0 -3
- package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/VoiceRecorder.java +0 -205
- package/dist/esm/VoiceRecorderImpl.d.ts +0 -27
- package/dist/esm/VoiceRecorderImpl.js.map +0 -1
- package/dist/esm/helper/get-blob-duration.js.map +0 -1
- package/dist/esm/predefined-web-responses.js.map +0 -1
- package/ios/Sources/VoiceRecorder/RecordOptions.swift +0 -8
- package/ios/Sources/VoiceRecorder/VoiceRecorder.swift +0 -170
- /package/dist/esm/{helper → platform/web}/get-blob-duration.d.ts +0 -0
- /package/dist/esm/{helper → platform/web}/get-blob-duration.js +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
1
|
+
package app.independo.capacitorvoicerecorder.platform;
|
|
2
2
|
|
|
3
3
|
import android.content.Context;
|
|
4
4
|
import android.media.AudioAttributes;
|
|
@@ -7,21 +7,34 @@ import android.media.AudioManager;
|
|
|
7
7
|
import android.media.MediaRecorder;
|
|
8
8
|
import android.os.Build;
|
|
9
9
|
import android.os.Environment;
|
|
10
|
+
import app.independo.capacitorvoicerecorder.adapters.RecorderAdapter;
|
|
11
|
+
import app.independo.capacitorvoicerecorder.core.CurrentRecordingStatus;
|
|
12
|
+
import app.independo.capacitorvoicerecorder.core.RecordOptions;
|
|
10
13
|
import java.io.File;
|
|
11
14
|
import java.io.IOException;
|
|
12
15
|
import java.util.regex.Matcher;
|
|
13
16
|
import java.util.regex.Pattern;
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
/** MediaRecorder wrapper that manages audio focus and interruptions. */
|
|
19
|
+
public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListener, RecorderAdapter {
|
|
16
20
|
|
|
21
|
+
/** Android context for file paths and system services. */
|
|
17
22
|
private final Context context;
|
|
23
|
+
/** Recording options passed from the service layer. */
|
|
18
24
|
private final RecordOptions options;
|
|
25
|
+
/** Active MediaRecorder instance for the session. */
|
|
19
26
|
private MediaRecorder mediaRecorder;
|
|
27
|
+
/** Output file for the current recording session. */
|
|
20
28
|
private File outputFile;
|
|
29
|
+
/** Current session status tracked locally. */
|
|
21
30
|
private CurrentRecordingStatus currentRecordingStatus = CurrentRecordingStatus.NONE;
|
|
31
|
+
/** Audio manager for focus changes. */
|
|
22
32
|
private AudioManager audioManager;
|
|
33
|
+
/** Focus request for Android O and above. */
|
|
23
34
|
private AudioFocusRequest audioFocusRequest;
|
|
35
|
+
/** Callback invoked when an interruption begins. */
|
|
24
36
|
private Runnable onInterruptionBegan;
|
|
37
|
+
/** Callback invoked when an interruption ends. */
|
|
25
38
|
private Runnable onInterruptionEnded;
|
|
26
39
|
|
|
27
40
|
public CustomMediaRecorder(Context context, RecordOptions options) throws IOException {
|
|
@@ -31,14 +44,17 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
31
44
|
generateMediaRecorder();
|
|
32
45
|
}
|
|
33
46
|
|
|
47
|
+
/** Sets the callback for interruption begin events. */
|
|
34
48
|
public void setOnInterruptionBegan(Runnable callback) {
|
|
35
49
|
this.onInterruptionBegan = callback;
|
|
36
50
|
}
|
|
37
51
|
|
|
52
|
+
/** Sets the callback for interruption end events. */
|
|
38
53
|
public void setOnInterruptionEnded(Runnable callback) {
|
|
39
54
|
this.onInterruptionEnded = callback;
|
|
40
55
|
}
|
|
41
56
|
|
|
57
|
+
/** Configures the MediaRecorder with audio settings. */
|
|
42
58
|
private void generateMediaRecorder() throws IOException {
|
|
43
59
|
mediaRecorder = new MediaRecorder();
|
|
44
60
|
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
|
@@ -50,6 +66,7 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
50
66
|
mediaRecorder.prepare();
|
|
51
67
|
}
|
|
52
68
|
|
|
69
|
+
/** Picks a directory and allocates the output file for this session. */
|
|
53
70
|
private void setRecorderOutputFile() throws IOException {
|
|
54
71
|
File outputDir = context.getCacheDir();
|
|
55
72
|
|
|
@@ -79,6 +96,7 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
79
96
|
mediaRecorder.setOutputFile(outputFile.getAbsolutePath());
|
|
80
97
|
}
|
|
81
98
|
|
|
99
|
+
/** Maps directory strings to Android file locations. */
|
|
82
100
|
private File getDirectory(String directory) {
|
|
83
101
|
return switch (directory) {
|
|
84
102
|
case "DOCUMENTS" -> Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
|
|
@@ -90,12 +108,14 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
90
108
|
};
|
|
91
109
|
}
|
|
92
110
|
|
|
111
|
+
/** Starts recording and requests audio focus. */
|
|
93
112
|
public void startRecording() {
|
|
94
113
|
requestAudioFocus();
|
|
95
114
|
mediaRecorder.start();
|
|
96
115
|
currentRecordingStatus = CurrentRecordingStatus.RECORDING;
|
|
97
116
|
}
|
|
98
117
|
|
|
118
|
+
/** Stops recording and releases audio resources. */
|
|
99
119
|
public void stopRecording() {
|
|
100
120
|
if (mediaRecorder == null) {
|
|
101
121
|
abandonAudioFocus();
|
|
@@ -118,14 +138,17 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
118
138
|
}
|
|
119
139
|
}
|
|
120
140
|
|
|
141
|
+
/** Returns the output file for the current session. */
|
|
121
142
|
public File getOutputFile() {
|
|
122
143
|
return outputFile;
|
|
123
144
|
}
|
|
124
145
|
|
|
146
|
+
/** Returns the options provided at start time. */
|
|
125
147
|
public RecordOptions getRecordOptions() {
|
|
126
148
|
return options;
|
|
127
149
|
}
|
|
128
150
|
|
|
151
|
+
/** Pauses recording when supported by the OS version. */
|
|
129
152
|
public boolean pauseRecording() throws NotSupportedOsVersion {
|
|
130
153
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
131
154
|
throw new NotSupportedOsVersion();
|
|
@@ -140,6 +163,7 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
140
163
|
}
|
|
141
164
|
}
|
|
142
165
|
|
|
166
|
+
/** Resumes a paused or interrupted recording session. */
|
|
143
167
|
public boolean resumeRecording() throws NotSupportedOsVersion {
|
|
144
168
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
145
169
|
throw new NotSupportedOsVersion();
|
|
@@ -155,18 +179,22 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
155
179
|
}
|
|
156
180
|
}
|
|
157
181
|
|
|
182
|
+
/** Returns the current recording status. */
|
|
158
183
|
public CurrentRecordingStatus getCurrentStatus() {
|
|
159
184
|
return currentRecordingStatus;
|
|
160
185
|
}
|
|
161
186
|
|
|
187
|
+
/** Deletes the output file from disk. */
|
|
162
188
|
public boolean deleteOutputFile() {
|
|
163
189
|
return outputFile.delete();
|
|
164
190
|
}
|
|
165
191
|
|
|
192
|
+
/** Simple capability check used for device validation. */
|
|
166
193
|
public static boolean canPhoneCreateMediaRecorder(Context context) {
|
|
167
194
|
return true;
|
|
168
195
|
}
|
|
169
196
|
|
|
197
|
+
/** Attempts to record a short sample to validate permission and hardware. */
|
|
170
198
|
private static boolean canPhoneCreateMediaRecorderWhileHavingPermission(Context context) {
|
|
171
199
|
CustomMediaRecorder tempMediaRecorder = null;
|
|
172
200
|
try {
|
|
@@ -181,6 +209,7 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
181
209
|
}
|
|
182
210
|
}
|
|
183
211
|
|
|
212
|
+
/** Requests audio focus for the recording session. */
|
|
184
213
|
private void requestAudioFocus() {
|
|
185
214
|
if (audioManager == null) {
|
|
186
215
|
return;
|
|
@@ -203,6 +232,7 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
203
232
|
}
|
|
204
233
|
}
|
|
205
234
|
|
|
235
|
+
/** Releases audio focus when recording completes. */
|
|
206
236
|
private void abandonAudioFocus() {
|
|
207
237
|
if (audioManager == null) {
|
|
208
238
|
return;
|
|
@@ -216,6 +246,7 @@ public class CustomMediaRecorder implements AudioManager.OnAudioFocusChangeListe
|
|
|
216
246
|
}
|
|
217
247
|
}
|
|
218
248
|
|
|
249
|
+
/** Handles audio focus changes as recording interruptions. */
|
|
219
250
|
@Override
|
|
220
251
|
public void onAudioFocusChange(int focusChange) {
|
|
221
252
|
switch (focusChange) {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
package app.independo.capacitorvoicerecorder.platform;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.media.AudioManager;
|
|
5
|
+
import android.media.MediaPlayer;
|
|
6
|
+
import android.net.Uri;
|
|
7
|
+
import android.util.Base64;
|
|
8
|
+
import app.independo.capacitorvoicerecorder.adapters.RecorderAdapter;
|
|
9
|
+
import app.independo.capacitorvoicerecorder.adapters.RecorderPlatform;
|
|
10
|
+
import app.independo.capacitorvoicerecorder.core.RecordOptions;
|
|
11
|
+
import java.io.BufferedInputStream;
|
|
12
|
+
import java.io.ByteArrayOutputStream;
|
|
13
|
+
import java.io.File;
|
|
14
|
+
import java.io.FileInputStream;
|
|
15
|
+
import java.io.IOException;
|
|
16
|
+
|
|
17
|
+
/** Default Android platform adapter for recording and file IO. */
|
|
18
|
+
public class DefaultRecorderPlatform implements RecorderPlatform {
|
|
19
|
+
|
|
20
|
+
/** Android context used to access system services. */
|
|
21
|
+
private final Context context;
|
|
22
|
+
|
|
23
|
+
public DefaultRecorderPlatform(Context context) {
|
|
24
|
+
this.context = context;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Returns whether the device can create a MediaRecorder instance. */
|
|
28
|
+
@Override
|
|
29
|
+
public boolean canDeviceVoiceRecord() {
|
|
30
|
+
return CustomMediaRecorder.canPhoneCreateMediaRecorder(context);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Returns true when the system audio mode indicates another recorder. */
|
|
34
|
+
@Override
|
|
35
|
+
public boolean isMicrophoneOccupied() {
|
|
36
|
+
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
|
37
|
+
if (audioManager == null) return true;
|
|
38
|
+
return audioManager.getMode() != AudioManager.MODE_NORMAL;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Creates the recorder adapter for the provided options. */
|
|
42
|
+
@Override
|
|
43
|
+
public RecorderAdapter createRecorder(RecordOptions options) throws Exception {
|
|
44
|
+
return new CustomMediaRecorder(context, options);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Reads the recorded file as base64, returning null on failure. */
|
|
48
|
+
@Override
|
|
49
|
+
public String readFileAsBase64(File recordedFile) {
|
|
50
|
+
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(recordedFile))) {
|
|
51
|
+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
52
|
+
byte[] buffer = new byte[8192];
|
|
53
|
+
int bytesRead;
|
|
54
|
+
while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
|
|
55
|
+
outputStream.write(buffer, 0, bytesRead);
|
|
56
|
+
}
|
|
57
|
+
return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
|
|
58
|
+
} catch (IOException exp) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Returns the file duration in milliseconds, or -1 on failure. */
|
|
64
|
+
@Override
|
|
65
|
+
public int getDurationMs(File recordedFile) {
|
|
66
|
+
MediaPlayer mediaPlayer = null;
|
|
67
|
+
try {
|
|
68
|
+
mediaPlayer = new MediaPlayer();
|
|
69
|
+
mediaPlayer.setDataSource(recordedFile.getAbsolutePath());
|
|
70
|
+
mediaPlayer.prepare();
|
|
71
|
+
return mediaPlayer.getDuration();
|
|
72
|
+
} catch (Exception ignore) {
|
|
73
|
+
return -1;
|
|
74
|
+
} finally {
|
|
75
|
+
if (mediaPlayer != null) {
|
|
76
|
+
mediaPlayer.release();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Returns a file:// URI for the given recording. */
|
|
82
|
+
@Override
|
|
83
|
+
public String toUri(File recordedFile) {
|
|
84
|
+
return Uri.fromFile(recordedFile).toString();
|
|
85
|
+
}
|
|
86
|
+
}
|
package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderService.java
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
package app.independo.capacitorvoicerecorder.service;
|
|
2
|
+
|
|
3
|
+
import app.independo.capacitorvoicerecorder.adapters.PermissionChecker;
|
|
4
|
+
import app.independo.capacitorvoicerecorder.adapters.RecorderAdapter;
|
|
5
|
+
import app.independo.capacitorvoicerecorder.adapters.RecorderPlatform;
|
|
6
|
+
import app.independo.capacitorvoicerecorder.core.CurrentRecordingStatus;
|
|
7
|
+
import app.independo.capacitorvoicerecorder.core.ErrorCodes;
|
|
8
|
+
import app.independo.capacitorvoicerecorder.core.RecordData;
|
|
9
|
+
import app.independo.capacitorvoicerecorder.core.RecordOptions;
|
|
10
|
+
import app.independo.capacitorvoicerecorder.platform.NotSupportedOsVersion;
|
|
11
|
+
import java.io.File;
|
|
12
|
+
|
|
13
|
+
/** Service layer that orchestrates recording operations. */
|
|
14
|
+
public class VoiceRecorderService {
|
|
15
|
+
|
|
16
|
+
/** Platform adapter that owns file and recorder creation. */
|
|
17
|
+
private final RecorderPlatform platform;
|
|
18
|
+
/** Permission checker injected from the bridge layer. */
|
|
19
|
+
private final PermissionChecker permissionChecker;
|
|
20
|
+
/** Current recorder instance for an active session. */
|
|
21
|
+
private RecorderAdapter recorder;
|
|
22
|
+
|
|
23
|
+
public VoiceRecorderService(RecorderPlatform platform, PermissionChecker permissionChecker) {
|
|
24
|
+
this.platform = platform;
|
|
25
|
+
this.permissionChecker = permissionChecker;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Returns whether the device can record audio. */
|
|
29
|
+
public boolean canDeviceVoiceRecord() {
|
|
30
|
+
return platform.canDeviceVoiceRecord();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Returns whether the app has microphone permission. */
|
|
34
|
+
public boolean hasAudioRecordingPermission() {
|
|
35
|
+
return permissionChecker.hasAudioPermission();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Starts a recording session or throws a service exception. */
|
|
39
|
+
public void startRecording(
|
|
40
|
+
RecordOptions options,
|
|
41
|
+
Runnable onInterruptionBegan,
|
|
42
|
+
Runnable onInterruptionEnded
|
|
43
|
+
) throws VoiceRecorderServiceException {
|
|
44
|
+
if (!platform.canDeviceVoiceRecord()) {
|
|
45
|
+
throw new VoiceRecorderServiceException(ErrorCodes.DEVICE_CANNOT_VOICE_RECORD);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!permissionChecker.hasAudioPermission()) {
|
|
49
|
+
throw new VoiceRecorderServiceException(ErrorCodes.MISSING_PERMISSION);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (platform.isMicrophoneOccupied()) {
|
|
53
|
+
throw new VoiceRecorderServiceException(ErrorCodes.MICROPHONE_BEING_USED);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (recorder != null) {
|
|
57
|
+
throw new VoiceRecorderServiceException(ErrorCodes.ALREADY_RECORDING);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
recorder = platform.createRecorder(options);
|
|
62
|
+
recorder.setOnInterruptionBegan(onInterruptionBegan);
|
|
63
|
+
recorder.setOnInterruptionEnded(onInterruptionEnded);
|
|
64
|
+
recorder.startRecording();
|
|
65
|
+
} catch (Exception exp) {
|
|
66
|
+
recorder = null;
|
|
67
|
+
throw new VoiceRecorderServiceException(ErrorCodes.FAILED_TO_RECORD, exp);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Stops the active recording session and returns the payload. */
|
|
72
|
+
public RecordData stopRecording() throws VoiceRecorderServiceException {
|
|
73
|
+
if (recorder == null) {
|
|
74
|
+
throw new VoiceRecorderServiceException(ErrorCodes.RECORDING_HAS_NOT_STARTED);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
RecordOptions options = recorder.getRecordOptions();
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
recorder.stopRecording();
|
|
81
|
+
File recordedFile = recorder.getOutputFile();
|
|
82
|
+
if (recordedFile == null) {
|
|
83
|
+
throw new VoiceRecorderServiceException(ErrorCodes.FAILED_TO_FETCH_RECORDING);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
String recordDataBase64 = null;
|
|
87
|
+
String uri = null;
|
|
88
|
+
if (options.directory() != null) {
|
|
89
|
+
uri = platform.toUri(recordedFile);
|
|
90
|
+
} else {
|
|
91
|
+
recordDataBase64 = platform.readFileAsBase64(recordedFile);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
int duration = platform.getDurationMs(recordedFile);
|
|
95
|
+
RecordData recordData = new RecordData(recordDataBase64, duration, "audio/aac", uri);
|
|
96
|
+
if ((recordDataBase64 == null && uri == null) || recordData.getMsDuration() < 0) {
|
|
97
|
+
throw new VoiceRecorderServiceException(ErrorCodes.EMPTY_RECORDING);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return recordData;
|
|
101
|
+
} catch (VoiceRecorderServiceException exp) {
|
|
102
|
+
throw exp;
|
|
103
|
+
} catch (Exception exp) {
|
|
104
|
+
throw new VoiceRecorderServiceException(ErrorCodes.FAILED_TO_FETCH_RECORDING, exp);
|
|
105
|
+
} finally {
|
|
106
|
+
if (options.directory() == null && recorder != null) {
|
|
107
|
+
recorder.deleteOutputFile();
|
|
108
|
+
}
|
|
109
|
+
recorder = null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Pauses the active recording session. */
|
|
114
|
+
public boolean pauseRecording() throws VoiceRecorderServiceException {
|
|
115
|
+
if (recorder == null) {
|
|
116
|
+
throw new VoiceRecorderServiceException(ErrorCodes.RECORDING_HAS_NOT_STARTED);
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
return recorder.pauseRecording();
|
|
120
|
+
} catch (NotSupportedOsVersion exception) {
|
|
121
|
+
throw new VoiceRecorderServiceException(ErrorCodes.NOT_SUPPORTED_OS_VERSION, exception);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Resumes a paused recording session. */
|
|
126
|
+
public boolean resumeRecording() throws VoiceRecorderServiceException {
|
|
127
|
+
if (recorder == null) {
|
|
128
|
+
throw new VoiceRecorderServiceException(ErrorCodes.RECORDING_HAS_NOT_STARTED);
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
return recorder.resumeRecording();
|
|
132
|
+
} catch (NotSupportedOsVersion exception) {
|
|
133
|
+
throw new VoiceRecorderServiceException(ErrorCodes.NOT_SUPPORTED_OS_VERSION, exception);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Returns the current recording status. */
|
|
138
|
+
public CurrentRecordingStatus getCurrentStatus() {
|
|
139
|
+
if (recorder == null) {
|
|
140
|
+
return CurrentRecordingStatus.NONE;
|
|
141
|
+
}
|
|
142
|
+
return recorder.getCurrentStatus();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package app.independo.capacitorvoicerecorder.service;
|
|
2
|
+
|
|
3
|
+
/** Exception wrapper that carries a canonical error code. */
|
|
4
|
+
public class VoiceRecorderServiceException extends Exception {
|
|
5
|
+
|
|
6
|
+
/** Canonical error code mapped to plugin responses. */
|
|
7
|
+
private final String code;
|
|
8
|
+
|
|
9
|
+
public VoiceRecorderServiceException(String code) {
|
|
10
|
+
super(code);
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public VoiceRecorderServiceException(String code, Exception cause) {
|
|
15
|
+
super(code, cause);
|
|
16
|
+
this.code = code;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Returns the canonical error code for this failure. */
|
|
20
|
+
public String getCode() {
|
|
21
|
+
return code;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CurrentRecordingStatus, GenericResponse, RecordingData, RecordingOptions } from '../definitions';
|
|
2
|
+
import type { VoiceRecorderPlatform } from '../service/VoiceRecorderService';
|
|
3
|
+
/** Web adapter that delegates to the browser-specific implementation. */
|
|
4
|
+
export declare class VoiceRecorderWebAdapter implements VoiceRecorderPlatform {
|
|
5
|
+
/** Browser implementation that talks to MediaRecorder APIs. */
|
|
6
|
+
private readonly voiceRecorderImpl;
|
|
7
|
+
/** Checks whether the browser can record audio. */
|
|
8
|
+
canDeviceVoiceRecord(): Promise<GenericResponse>;
|
|
9
|
+
/** Returns whether the browser has microphone permission. */
|
|
10
|
+
hasAudioRecordingPermission(): Promise<GenericResponse>;
|
|
11
|
+
/** Requests microphone permission through the browser. */
|
|
12
|
+
requestAudioRecordingPermission(): Promise<GenericResponse>;
|
|
13
|
+
/** Starts a recording session using MediaRecorder. */
|
|
14
|
+
startRecording(options?: RecordingOptions): Promise<GenericResponse>;
|
|
15
|
+
/** Stops the recording session and returns the payload. */
|
|
16
|
+
stopRecording(): Promise<RecordingData>;
|
|
17
|
+
/** Pauses the recording session when supported. */
|
|
18
|
+
pauseRecording(): Promise<GenericResponse>;
|
|
19
|
+
/** Resumes a paused recording session when supported. */
|
|
20
|
+
resumeRecording(): Promise<GenericResponse>;
|
|
21
|
+
/** Returns the current recording state. */
|
|
22
|
+
getCurrentStatus(): Promise<CurrentRecordingStatus>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { VoiceRecorderImpl } from '../platform/web/VoiceRecorderImpl';
|
|
2
|
+
/** Web adapter that delegates to the browser-specific implementation. */
|
|
3
|
+
export class VoiceRecorderWebAdapter {
|
|
4
|
+
constructor() {
|
|
5
|
+
/** Browser implementation that talks to MediaRecorder APIs. */
|
|
6
|
+
this.voiceRecorderImpl = new VoiceRecorderImpl();
|
|
7
|
+
}
|
|
8
|
+
/** Checks whether the browser can record audio. */
|
|
9
|
+
canDeviceVoiceRecord() {
|
|
10
|
+
return VoiceRecorderImpl.canDeviceVoiceRecord();
|
|
11
|
+
}
|
|
12
|
+
/** Returns whether the browser has microphone permission. */
|
|
13
|
+
hasAudioRecordingPermission() {
|
|
14
|
+
return VoiceRecorderImpl.hasAudioRecordingPermission();
|
|
15
|
+
}
|
|
16
|
+
/** Requests microphone permission through the browser. */
|
|
17
|
+
requestAudioRecordingPermission() {
|
|
18
|
+
return VoiceRecorderImpl.requestAudioRecordingPermission();
|
|
19
|
+
}
|
|
20
|
+
/** Starts a recording session using MediaRecorder. */
|
|
21
|
+
startRecording(options) {
|
|
22
|
+
return this.voiceRecorderImpl.startRecording(options);
|
|
23
|
+
}
|
|
24
|
+
/** Stops the recording session and returns the payload. */
|
|
25
|
+
stopRecording() {
|
|
26
|
+
return this.voiceRecorderImpl.stopRecording();
|
|
27
|
+
}
|
|
28
|
+
/** Pauses the recording session when supported. */
|
|
29
|
+
pauseRecording() {
|
|
30
|
+
return this.voiceRecorderImpl.pauseRecording();
|
|
31
|
+
}
|
|
32
|
+
/** Resumes a paused recording session when supported. */
|
|
33
|
+
resumeRecording() {
|
|
34
|
+
return this.voiceRecorderImpl.resumeRecording();
|
|
35
|
+
}
|
|
36
|
+
/** Returns the current recording state. */
|
|
37
|
+
getCurrentStatus() {
|
|
38
|
+
return this.voiceRecorderImpl.getCurrentStatus();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=VoiceRecorderWebAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceRecorderWebAdapter.js","sourceRoot":"","sources":["../../../src/adapters/VoiceRecorderWebAdapter.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAGtE,yEAAyE;AACzE,MAAM,OAAO,uBAAuB;IAApC;QACI,+DAA+D;QAC9C,sBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;IAyCjE,CAAC;IAvCG,mDAAmD;IAC5C,oBAAoB;QACvB,OAAO,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;IACpD,CAAC;IAED,6DAA6D;IACtD,2BAA2B;QAC9B,OAAO,iBAAiB,CAAC,2BAA2B,EAAE,CAAC;IAC3D,CAAC;IAED,0DAA0D;IACnD,+BAA+B;QAClC,OAAO,iBAAiB,CAAC,+BAA+B,EAAE,CAAC;IAC/D,CAAC;IAED,sDAAsD;IAC/C,cAAc,CAAC,OAA0B;QAC5C,OAAO,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAED,2DAA2D;IACpD,aAAa;QAChB,OAAO,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,CAAC;IAClD,CAAC;IAED,mDAAmD;IAC5C,cAAc;QACjB,OAAO,IAAI,CAAC,iBAAiB,CAAC,cAAc,EAAE,CAAC;IACnD,CAAC;IAED,yDAAyD;IAClD,eAAe;QAClB,OAAO,IAAI,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC;IACpD,CAAC;IAED,2CAA2C;IACpC,gBAAgB;QACnB,OAAO,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,EAAE,CAAC;IACrD,CAAC;CACJ","sourcesContent":["import type {\n CurrentRecordingStatus,\n GenericResponse,\n RecordingData,\n RecordingOptions,\n} from '../definitions';\nimport { VoiceRecorderImpl } from '../platform/web/VoiceRecorderImpl';\nimport type { VoiceRecorderPlatform } from '../service/VoiceRecorderService';\n\n/** Web adapter that delegates to the browser-specific implementation. */\nexport class VoiceRecorderWebAdapter implements VoiceRecorderPlatform {\n /** Browser implementation that talks to MediaRecorder APIs. */\n private readonly voiceRecorderImpl = new VoiceRecorderImpl();\n\n /** Checks whether the browser can record audio. */\n public canDeviceVoiceRecord(): Promise<GenericResponse> {\n return VoiceRecorderImpl.canDeviceVoiceRecord();\n }\n\n /** Returns whether the browser has microphone permission. */\n public hasAudioRecordingPermission(): Promise<GenericResponse> {\n return VoiceRecorderImpl.hasAudioRecordingPermission();\n }\n\n /** Requests microphone permission through the browser. */\n public requestAudioRecordingPermission(): Promise<GenericResponse> {\n return VoiceRecorderImpl.requestAudioRecordingPermission();\n }\n\n /** Starts a recording session using MediaRecorder. */\n public startRecording(options?: RecordingOptions): Promise<GenericResponse> {\n return this.voiceRecorderImpl.startRecording(options);\n }\n\n /** Stops the recording session and returns the payload. */\n public stopRecording(): Promise<RecordingData> {\n return this.voiceRecorderImpl.stopRecording();\n }\n\n /** Pauses the recording session when supported. */\n public pauseRecording(): Promise<GenericResponse> {\n return this.voiceRecorderImpl.pauseRecording();\n }\n\n /** Resumes a paused recording session when supported. */\n public resumeRecording(): Promise<GenericResponse> {\n return this.voiceRecorderImpl.resumeRecording();\n }\n\n /** Returns the current recording state. */\n public getCurrentStatus(): Promise<CurrentRecordingStatus> {\n return this.voiceRecorderImpl.getCurrentStatus();\n }\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Normalizes legacy error messages into canonical error codes. */
|
|
2
|
+
export declare const toCanonicalErrorCode: (legacyMessage: string) => string;
|
|
3
|
+
/** Adds a canonical `code` field to Error-like objects when possible. */
|
|
4
|
+
export declare const attachCanonicalErrorCode: (error: unknown) => void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Maps legacy error messages to canonical error codes. */
|
|
2
|
+
const legacyToCanonical = {
|
|
3
|
+
CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',
|
|
4
|
+
};
|
|
5
|
+
/** Normalizes legacy error messages into canonical error codes. */
|
|
6
|
+
export const toCanonicalErrorCode = (legacyMessage) => {
|
|
7
|
+
var _a;
|
|
8
|
+
return (_a = legacyToCanonical[legacyMessage]) !== null && _a !== void 0 ? _a : legacyMessage;
|
|
9
|
+
};
|
|
10
|
+
/** Adds a canonical `code` field to Error-like objects when possible. */
|
|
11
|
+
export const attachCanonicalErrorCode = (error) => {
|
|
12
|
+
if (!error || typeof error !== 'object') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const messageValue = error.message;
|
|
16
|
+
if (typeof messageValue !== 'string') {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
error.code = toCanonicalErrorCode(messageValue);
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=error-codes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-codes.js","sourceRoot":"","sources":["../../../src/core/error-codes.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,MAAM,iBAAiB,GAA2B;IAC9C,2BAA2B,EAAE,4BAA4B;CAC5D,CAAC;AAEF,mEAAmE;AACnE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,aAAqB,EAAU,EAAE;;IAClE,OAAO,MAAA,iBAAiB,CAAC,aAAa,CAAC,mCAAI,aAAa,CAAC;AAC7D,CAAC,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAc,EAAQ,EAAE;IAC7D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO;IACX,CAAC;IACD,MAAM,YAAY,GAAI,KAA+B,CAAC,OAAO,CAAC;IAC9D,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO;IACX,CAAC;IACA,KAA2B,CAAC,IAAI,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;AAC3E,CAAC,CAAC","sourcesContent":["/** Maps legacy error messages to canonical error codes. */\nconst legacyToCanonical: Record<string, string> = {\n CANNOT_RECORD_ON_THIS_PHONE: 'DEVICE_CANNOT_VOICE_RECORD',\n};\n\n/** Normalizes legacy error messages into canonical error codes. */\nexport const toCanonicalErrorCode = (legacyMessage: string): string => {\n return legacyToCanonical[legacyMessage] ?? legacyMessage;\n};\n\n/** Adds a canonical `code` field to Error-like objects when possible. */\nexport const attachCanonicalErrorCode = (error: unknown): void => {\n if (!error || typeof error !== 'object') {\n return;\n }\n const messageValue = (error as { message?: unknown }).message;\n if (typeof messageValue !== 'string') {\n return;\n }\n (error as { code?: string }).code = toCanonicalErrorCode(messageValue);\n};\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Normalizes recording payloads into a stable contract shape. */
|
|
2
|
+
export const normalizeRecordingData = (data) => {
|
|
3
|
+
const { recordDataBase64, uri, msDuration, mimeType } = data.value;
|
|
4
|
+
const normalizedValue = { msDuration, mimeType };
|
|
5
|
+
const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;
|
|
6
|
+
const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;
|
|
7
|
+
if (trimmedUri) {
|
|
8
|
+
normalizedValue.uri = trimmedUri;
|
|
9
|
+
}
|
|
10
|
+
else if (trimmedBase64) {
|
|
11
|
+
normalizedValue.recordDataBase64 = trimmedBase64;
|
|
12
|
+
}
|
|
13
|
+
return { value: normalizedValue };
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=recording-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recording-contract.js","sourceRoot":"","sources":["../../../src/core/recording-contract.ts"],"names":[],"mappings":"AAEA,kEAAkE;AAClE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,IAAmB,EAAiB,EAAE;IACzE,MAAM,EAAE,gBAAgB,EAAE,GAAG,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;IACnE,MAAM,eAAe,GAA4B,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IAC1E,MAAM,UAAU,GAAG,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/E,MAAM,aAAa,GAAG,OAAO,gBAAgB,KAAK,QAAQ,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC;IAEzH,IAAI,UAAU,EAAE,CAAC;QACb,eAAe,CAAC,GAAG,GAAG,UAAU,CAAC;IACrC,CAAC;SAAM,IAAI,aAAa,EAAE,CAAC;QACvB,eAAe,CAAC,gBAAgB,GAAG,aAAa,CAAC;IACrD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,eAAe,EAAmB,CAAC;AACvD,CAAC,CAAC","sourcesContent":["import type { RecordingData } from '../definitions';\n\n/** Normalizes recording payloads into a stable contract shape. */\nexport const normalizeRecordingData = (data: RecordingData): RecordingData => {\n const { recordDataBase64, uri, msDuration, mimeType } = data.value;\n const normalizedValue: Record<string, unknown> = { msDuration, mimeType };\n const trimmedUri = typeof uri === 'string' && uri.length > 0 ? uri : undefined;\n const trimmedBase64 = typeof recordDataBase64 === 'string' && recordDataBase64.length > 0 ? recordDataBase64 : undefined;\n\n if (trimmedUri) {\n normalizedValue.uri = trimmedUri;\n } else if (trimmedBase64) {\n normalizedValue.recordDataBase64 = trimmedBase64;\n }\n\n return { value: normalizedValue } as RecordingData;\n};\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Supported response shapes for plugin results. */
|
|
2
|
+
export type ResponseFormat = 'legacy' | 'normalized';
|
|
3
|
+
/** Default response shape when no config is provided. */
|
|
4
|
+
export declare const DEFAULT_RESPONSE_FORMAT: ResponseFormat;
|
|
5
|
+
/** Parses a user-provided response format into a supported value. */
|
|
6
|
+
export declare const resolveResponseFormat: (value: unknown) => ResponseFormat;
|
|
7
|
+
/** Reads the response format from a Capacitor plugin config object. */
|
|
8
|
+
export declare const getResponseFormatFromConfig: (config: unknown) => ResponseFormat;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Default response shape when no config is provided. */
|
|
2
|
+
export const DEFAULT_RESPONSE_FORMAT = 'legacy';
|
|
3
|
+
/** Parses a user-provided response format into a supported value. */
|
|
4
|
+
export const resolveResponseFormat = (value) => {
|
|
5
|
+
if (typeof value === 'string' && value.toLowerCase() === 'normalized') {
|
|
6
|
+
return 'normalized';
|
|
7
|
+
}
|
|
8
|
+
return DEFAULT_RESPONSE_FORMAT;
|
|
9
|
+
};
|
|
10
|
+
/** Reads the response format from a Capacitor plugin config object. */
|
|
11
|
+
export const getResponseFormatFromConfig = (config) => {
|
|
12
|
+
if (config && typeof config === 'object' && 'responseFormat' in config) {
|
|
13
|
+
return resolveResponseFormat(config.responseFormat);
|
|
14
|
+
}
|
|
15
|
+
return DEFAULT_RESPONSE_FORMAT;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=response-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-format.js","sourceRoot":"","sources":["../../../src/core/response-format.ts"],"names":[],"mappings":"AAGA,yDAAyD;AACzD,MAAM,CAAC,MAAM,uBAAuB,GAAmB,QAAQ,CAAC;AAEhE,qEAAqE;AACrE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAkB,EAAE;IACpE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,YAAY,EAAE,CAAC;QACpE,OAAO,YAAY,CAAC;IACxB,CAAC;IACD,OAAO,uBAAuB,CAAC;AACnC,CAAC,CAAC;AAEF,uEAAuE;AACvE,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,MAAe,EAAkB,EAAE;IAC3E,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,gBAAgB,IAAI,MAAM,EAAE,CAAC;QACrE,OAAO,qBAAqB,CAAE,MAAuC,CAAC,cAAc,CAAC,CAAC;IAC1F,CAAC;IACD,OAAO,uBAAuB,CAAC;AACnC,CAAC,CAAC","sourcesContent":["/** Supported response shapes for plugin results. */\nexport type ResponseFormat = 'legacy' | 'normalized';\n\n/** Default response shape when no config is provided. */\nexport const DEFAULT_RESPONSE_FORMAT: ResponseFormat = 'legacy';\n\n/** Parses a user-provided response format into a supported value. */\nexport const resolveResponseFormat = (value: unknown): ResponseFormat => {\n if (typeof value === 'string' && value.toLowerCase() === 'normalized') {\n return 'normalized';\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n\n/** Reads the response format from a Capacitor plugin config object. */\nexport const getResponseFormatFromConfig = (config: unknown): ResponseFormat => {\n if (config && typeof config === 'object' && 'responseFormat' in config) {\n return resolveResponseFormat((config as { responseFormat?: unknown }).responseFormat);\n }\n return DEFAULT_RESPONSE_FORMAT;\n};\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CurrentRecordingStatus, GenericResponse, RecordingData, RecordingOptions } from '../../definitions';
|
|
2
|
+
/** Preferred MIME types to probe in order of fallback. */
|
|
3
|
+
declare const POSSIBLE_MIME_TYPES: {
|
|
4
|
+
'audio/aac': string;
|
|
5
|
+
'audio/webm;codecs=opus': string;
|
|
6
|
+
'audio/mp4': string;
|
|
7
|
+
'audio/webm': string;
|
|
8
|
+
'audio/ogg;codecs=opus': string;
|
|
9
|
+
};
|
|
10
|
+
/** Browser implementation backed by MediaRecorder and Capacitor Filesystem. */
|
|
11
|
+
export declare class VoiceRecorderImpl {
|
|
12
|
+
/** Active MediaRecorder instance, if recording. */
|
|
13
|
+
private mediaRecorder;
|
|
14
|
+
/** Collected data chunks from MediaRecorder. */
|
|
15
|
+
private chunks;
|
|
16
|
+
/** Promise resolved when the recorder stops and payload is ready. */
|
|
17
|
+
private pendingResult;
|
|
18
|
+
/** Returns whether the browser can start a recording session. */
|
|
19
|
+
static canDeviceVoiceRecord(): Promise<GenericResponse>;
|
|
20
|
+
/** Starts a recording session using MediaRecorder. */
|
|
21
|
+
startRecording(options?: RecordingOptions): Promise<GenericResponse>;
|
|
22
|
+
/** Stops the current recording and resolves the pending payload. */
|
|
23
|
+
stopRecording(): Promise<RecordingData>;
|
|
24
|
+
/** Returns whether the browser has microphone permission. */
|
|
25
|
+
static hasAudioRecordingPermission(): Promise<GenericResponse>;
|
|
26
|
+
/** Requests microphone permission from the browser. */
|
|
27
|
+
static requestAudioRecordingPermission(): Promise<GenericResponse>;
|
|
28
|
+
/** Pauses the recording session when supported. */
|
|
29
|
+
pauseRecording(): Promise<GenericResponse>;
|
|
30
|
+
/** Resumes a paused recording session when supported. */
|
|
31
|
+
resumeRecording(): Promise<GenericResponse>;
|
|
32
|
+
/** Returns the current recording status from MediaRecorder. */
|
|
33
|
+
getCurrentStatus(): Promise<CurrentRecordingStatus>;
|
|
34
|
+
/** Returns the first supported MIME type, if any. */
|
|
35
|
+
static getSupportedMimeType<T extends keyof typeof POSSIBLE_MIME_TYPES>(): T | null;
|
|
36
|
+
/** Initializes MediaRecorder and wires up handlers. */
|
|
37
|
+
private onSuccessfullyStartedRecording;
|
|
38
|
+
/** Handles failures from getUserMedia. */
|
|
39
|
+
private onFailedToStartRecording;
|
|
40
|
+
/** Converts a Blob payload into a base64 string. */
|
|
41
|
+
private static blobToBase64;
|
|
42
|
+
/** Resets state for the next recording attempt. */
|
|
43
|
+
private prepareInstanceForNextOperation;
|
|
44
|
+
}
|
|
45
|
+
export {};
|