@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.
Files changed (76) hide show
  1. package/README.md +40 -30
  2. package/android/build.gradle +44 -1
  3. package/android/src/main/java/app/independo/capacitorvoicerecorder/VoiceRecorder.java +146 -0
  4. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/PermissionChecker.java +8 -0
  5. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecordDataMapper.java +32 -0
  6. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderAdapter.java +39 -0
  7. package/android/src/main/java/app/independo/capacitorvoicerecorder/adapters/RecorderPlatform.java +25 -0
  8. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/CurrentRecordingStatus.java +9 -0
  9. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ErrorCodes.java +19 -0
  10. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/Messages.java +2 -1
  11. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/RecordData.java +15 -1
  12. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/RecordOptions.java +4 -0
  13. package/android/src/main/java/app/independo/capacitorvoicerecorder/core/ResponseFormat.java +18 -0
  14. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/core}/ResponseGenerator.java +7 -1
  15. package/android/src/main/java/{com/tchvu3/capacitorvoicerecorder → app/independo/capacitorvoicerecorder/platform}/CustomMediaRecorder.java +33 -2
  16. package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/DefaultRecorderPlatform.java +86 -0
  17. package/android/src/main/java/app/independo/capacitorvoicerecorder/platform/NotSupportedOsVersion.java +4 -0
  18. package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderService.java +144 -0
  19. package/android/src/main/java/app/independo/capacitorvoicerecorder/service/VoiceRecorderServiceException.java +23 -0
  20. package/dist/esm/adapters/VoiceRecorderWebAdapter.d.ts +23 -0
  21. package/dist/esm/adapters/VoiceRecorderWebAdapter.js +41 -0
  22. package/dist/esm/adapters/VoiceRecorderWebAdapter.js.map +1 -0
  23. package/dist/esm/core/error-codes.d.ts +4 -0
  24. package/dist/esm/core/error-codes.js +21 -0
  25. package/dist/esm/core/error-codes.js.map +1 -0
  26. package/dist/esm/core/recording-contract.d.ts +3 -0
  27. package/dist/esm/core/recording-contract.js +15 -0
  28. package/dist/esm/core/recording-contract.js.map +1 -0
  29. package/dist/esm/core/response-format.d.ts +8 -0
  30. package/dist/esm/core/response-format.js +17 -0
  31. package/dist/esm/core/response-format.js.map +1 -0
  32. package/dist/esm/platform/web/VoiceRecorderImpl.d.ts +45 -0
  33. package/dist/esm/{VoiceRecorderImpl.js → platform/web/VoiceRecorderImpl.js} +20 -2
  34. package/dist/esm/platform/web/VoiceRecorderImpl.js.map +1 -0
  35. package/dist/esm/platform/web/get-blob-duration.js.map +1 -0
  36. package/dist/esm/{predefined-web-responses.d.ts → platform/web/predefined-web-responses.d.ts} +12 -1
  37. package/dist/esm/{predefined-web-responses.js → platform/web/predefined-web-responses.js} +11 -0
  38. package/dist/esm/platform/web/predefined-web-responses.js.map +1 -0
  39. package/dist/esm/service/VoiceRecorderService.d.ts +47 -0
  40. package/dist/esm/service/VoiceRecorderService.js +60 -0
  41. package/dist/esm/service/VoiceRecorderService.js.map +1 -0
  42. package/dist/esm/web.d.ts +12 -1
  43. package/dist/esm/web.js +26 -12
  44. package/dist/esm/web.js.map +1 -1
  45. package/dist/plugin.cjs.js +200 -9
  46. package/dist/plugin.cjs.js.map +1 -1
  47. package/dist/plugin.js +200 -9
  48. package/dist/plugin.js.map +1 -1
  49. package/ios/Sources/VoiceRecorder/Adapters/DefaultRecorderPlatform.swift +33 -0
  50. package/ios/Sources/VoiceRecorder/Adapters/RecordDataMapper.swift +38 -0
  51. package/ios/Sources/VoiceRecorder/Adapters/RecorderAdapter.swift +24 -0
  52. package/ios/Sources/VoiceRecorder/Adapters/RecorderPlatform.swift +11 -0
  53. package/ios/Sources/VoiceRecorder/Bridge/VoiceRecorder.swift +172 -0
  54. package/ios/Sources/VoiceRecorder/{CurrentRecordingStatus.swift → Core/CurrentRecordingStatus.swift} +1 -0
  55. package/ios/Sources/VoiceRecorder/Core/ErrorCodes.swift +16 -0
  56. package/ios/Sources/VoiceRecorder/{Messages.swift → Core/Messages.swift} +1 -0
  57. package/ios/Sources/VoiceRecorder/{RecordData.swift → Core/RecordData.swift} +6 -0
  58. package/ios/Sources/VoiceRecorder/Core/RecordOptions.swift +11 -0
  59. package/ios/Sources/VoiceRecorder/Core/ResponseFormat.swift +22 -0
  60. package/ios/Sources/VoiceRecorder/{ResponseGenerator.swift → Core/ResponseGenerator.swift} +6 -0
  61. package/ios/Sources/VoiceRecorder/{CustomMediaRecorder.swift → Platform/CustomMediaRecorder.swift} +25 -1
  62. package/ios/Sources/VoiceRecorder/Service/VoiceRecorderService.swift +128 -0
  63. package/ios/Sources/VoiceRecorder/Service/VoiceRecorderServiceError.swift +14 -0
  64. package/package.json +10 -4
  65. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/CurrentRecordingStatus.java +0 -8
  66. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/NotSupportedOsVersion.java +0 -3
  67. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/RecordOptions.java +0 -3
  68. package/android/src/main/java/com/tchvu3/capacitorvoicerecorder/VoiceRecorder.java +0 -205
  69. package/dist/esm/VoiceRecorderImpl.d.ts +0 -27
  70. package/dist/esm/VoiceRecorderImpl.js.map +0 -1
  71. package/dist/esm/helper/get-blob-duration.js.map +0 -1
  72. package/dist/esm/predefined-web-responses.js.map +0 -1
  73. package/ios/Sources/VoiceRecorder/RecordOptions.swift +0 -8
  74. package/ios/Sources/VoiceRecorder/VoiceRecorder.swift +0 -170
  75. /package/dist/esm/{helper → platform/web}/get-blob-duration.d.ts +0 -0
  76. /package/dist/esm/{helper → platform/web}/get-blob-duration.js +0 -0
@@ -1,205 +0,0 @@
1
- package com.tchvu3.capacitorvoicerecorder;
2
-
3
- import android.Manifest;
4
- import android.content.Context;
5
- import android.media.AudioManager;
6
- import android.media.MediaPlayer;
7
- import android.net.Uri;
8
- import android.util.Base64;
9
- import com.getcapacitor.PermissionState;
10
- import com.getcapacitor.Plugin;
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.BufferedInputStream;
17
- import java.io.File;
18
- import java.io.FileInputStream;
19
- import java.io.IOException;
20
-
21
- @CapacitorPlugin(
22
- name = "VoiceRecorder",
23
- permissions = { @Permission(alias = VoiceRecorder.RECORD_AUDIO_ALIAS, strings = { Manifest.permission.RECORD_AUDIO }) }
24
- )
25
- public class VoiceRecorder extends Plugin {
26
-
27
- static final String RECORD_AUDIO_ALIAS = "voice recording";
28
- private CustomMediaRecorder mediaRecorder;
29
-
30
- @PluginMethod
31
- public void canDeviceVoiceRecord(PluginCall call) {
32
- if (CustomMediaRecorder.canPhoneCreateMediaRecorder(getContext())) {
33
- call.resolve(ResponseGenerator.successResponse());
34
- } else {
35
- call.resolve(ResponseGenerator.failResponse());
36
- }
37
- }
38
-
39
- @PluginMethod
40
- public void requestAudioRecordingPermission(PluginCall call) {
41
- if (doesUserGaveAudioRecordingPermission()) {
42
- call.resolve(ResponseGenerator.successResponse());
43
- } else {
44
- requestPermissionForAlias(RECORD_AUDIO_ALIAS, call, "recordAudioPermissionCallback");
45
- }
46
- }
47
-
48
- @PermissionCallback
49
- private void recordAudioPermissionCallback(PluginCall call) {
50
- this.hasAudioRecordingPermission(call);
51
- }
52
-
53
- @PluginMethod
54
- public void hasAudioRecordingPermission(PluginCall call) {
55
- call.resolve(ResponseGenerator.fromBoolean(doesUserGaveAudioRecordingPermission()));
56
- }
57
-
58
- @PluginMethod
59
- public void startRecording(PluginCall call) {
60
- if (!CustomMediaRecorder.canPhoneCreateMediaRecorder(getContext())) {
61
- call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE);
62
- return;
63
- }
64
-
65
- if (!doesUserGaveAudioRecordingPermission()) {
66
- call.reject(Messages.MISSING_PERMISSION);
67
- return;
68
- }
69
-
70
- if (this.isMicrophoneOccupied()) {
71
- call.reject(Messages.MICROPHONE_BEING_USED);
72
- return;
73
- }
74
-
75
- if (mediaRecorder != null) {
76
- call.reject(Messages.ALREADY_RECORDING);
77
- return;
78
- }
79
-
80
- try {
81
- String directory = call.getString("directory");
82
- String subDirectory = call.getString("subDirectory");
83
- RecordOptions options = new RecordOptions(directory, subDirectory);
84
- mediaRecorder = new CustomMediaRecorder(getContext(), options);
85
- mediaRecorder.setOnInterruptionBegan(() -> notifyListeners("voiceRecordingInterrupted", null));
86
- mediaRecorder.setOnInterruptionEnded(() -> notifyListeners("voiceRecordingInterruptionEnded", null));
87
- mediaRecorder.startRecording();
88
- call.resolve(ResponseGenerator.successResponse());
89
- } catch (Exception exp) {
90
- call.reject(Messages.FAILED_TO_RECORD, exp);
91
- }
92
- }
93
-
94
- @PluginMethod
95
- public void stopRecording(PluginCall call) {
96
- if (mediaRecorder == null) {
97
- call.reject(Messages.RECORDING_HAS_NOT_STARTED);
98
- return;
99
- }
100
-
101
- try {
102
- mediaRecorder.stopRecording();
103
- File recordedFile = mediaRecorder.getOutputFile();
104
- RecordOptions options = mediaRecorder.getRecordOptions();
105
-
106
- String recordDataBase64 = null;
107
- String uri = null;
108
- if (options.directory() != null) {
109
- uri = Uri.fromFile(recordedFile).toString();
110
- } else {
111
- recordDataBase64 = readRecordedFileAsBase64(recordedFile);
112
- }
113
-
114
- RecordData recordData = new RecordData(
115
- recordDataBase64,
116
- getMsDurationOfAudioFile(recordedFile.getAbsolutePath()),
117
- "audio/aac",
118
- uri
119
- );
120
- if ((recordDataBase64 == null && uri == null) || recordData.getMsDuration() < 0) {
121
- call.reject(Messages.EMPTY_RECORDING);
122
- } else {
123
- call.resolve(ResponseGenerator.dataResponse(recordData.toJSObject()));
124
- }
125
- } catch (Exception exp) {
126
- call.reject(Messages.FAILED_TO_FETCH_RECORDING, exp);
127
- } finally {
128
- RecordOptions options = mediaRecorder.getRecordOptions();
129
- if (options.directory() == null) {
130
- mediaRecorder.deleteOutputFile();
131
- }
132
-
133
- mediaRecorder = null;
134
- }
135
- }
136
-
137
- @PluginMethod
138
- public void pauseRecording(PluginCall call) {
139
- if (mediaRecorder == null) {
140
- call.reject(Messages.RECORDING_HAS_NOT_STARTED);
141
- return;
142
- }
143
- try {
144
- call.resolve(ResponseGenerator.fromBoolean(mediaRecorder.pauseRecording()));
145
- } catch (NotSupportedOsVersion exception) {
146
- call.reject(Messages.NOT_SUPPORTED_OS_VERSION);
147
- }
148
- }
149
-
150
- @PluginMethod
151
- public void resumeRecording(PluginCall call) {
152
- if (mediaRecorder == null) {
153
- call.reject(Messages.RECORDING_HAS_NOT_STARTED);
154
- return;
155
- }
156
- try {
157
- call.resolve(ResponseGenerator.fromBoolean(mediaRecorder.resumeRecording()));
158
- } catch (NotSupportedOsVersion exception) {
159
- call.reject(Messages.NOT_SUPPORTED_OS_VERSION);
160
- }
161
- }
162
-
163
- @PluginMethod
164
- public void getCurrentStatus(PluginCall call) {
165
- if (mediaRecorder == null) {
166
- call.resolve(ResponseGenerator.statusResponse(CurrentRecordingStatus.NONE));
167
- } else {
168
- call.resolve(ResponseGenerator.statusResponse(mediaRecorder.getCurrentStatus()));
169
- }
170
- }
171
-
172
- private boolean doesUserGaveAudioRecordingPermission() {
173
- return getPermissionState(VoiceRecorder.RECORD_AUDIO_ALIAS).equals(PermissionState.GRANTED);
174
- }
175
-
176
- private String readRecordedFileAsBase64(File recordedFile) {
177
- BufferedInputStream bufferedInputStream;
178
- byte[] bArray = new byte[(int) recordedFile.length()];
179
- try {
180
- bufferedInputStream = new BufferedInputStream(new FileInputStream(recordedFile));
181
- bufferedInputStream.read(bArray);
182
- bufferedInputStream.close();
183
- } catch (IOException exp) {
184
- return null;
185
- }
186
- return Base64.encodeToString(bArray, Base64.DEFAULT);
187
- }
188
-
189
- private int getMsDurationOfAudioFile(String recordedFilePath) {
190
- try {
191
- MediaPlayer mediaPlayer = new MediaPlayer();
192
- mediaPlayer.setDataSource(recordedFilePath);
193
- mediaPlayer.prepare();
194
- return mediaPlayer.getDuration();
195
- } catch (Exception ignore) {
196
- return -1;
197
- }
198
- }
199
-
200
- private boolean isMicrophoneOccupied() {
201
- AudioManager audioManager = (AudioManager) this.getContext().getSystemService(Context.AUDIO_SERVICE);
202
- if (audioManager == null) return true;
203
- return audioManager.getMode() != AudioManager.MODE_NORMAL;
204
- }
205
- }
@@ -1,27 +0,0 @@
1
- import type { CurrentRecordingStatus, GenericResponse, RecordingData, RecordingOptions } from './definitions';
2
- declare const POSSIBLE_MIME_TYPES: {
3
- 'audio/aac': string;
4
- 'audio/webm;codecs=opus': string;
5
- 'audio/mp4': string;
6
- 'audio/webm': string;
7
- 'audio/ogg;codecs=opus': string;
8
- };
9
- export declare class VoiceRecorderImpl {
10
- private mediaRecorder;
11
- private chunks;
12
- private pendingResult;
13
- static canDeviceVoiceRecord(): Promise<GenericResponse>;
14
- startRecording(options?: RecordingOptions): Promise<GenericResponse>;
15
- stopRecording(): Promise<RecordingData>;
16
- static hasAudioRecordingPermission(): Promise<GenericResponse>;
17
- static requestAudioRecordingPermission(): Promise<GenericResponse>;
18
- pauseRecording(): Promise<GenericResponse>;
19
- resumeRecording(): Promise<GenericResponse>;
20
- getCurrentStatus(): Promise<CurrentRecordingStatus>;
21
- static getSupportedMimeType<T extends keyof typeof POSSIBLE_MIME_TYPES>(): T | null;
22
- private onSuccessfullyStartedRecording;
23
- private onFailedToStartRecording;
24
- private static blobToBase64;
25
- private prepareInstanceForNextOperation;
26
- }
27
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"file":"VoiceRecorderImpl.js","sourceRoot":"","sources":["../../src/VoiceRecorderImpl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACjD,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAS/C,OAAO,eAAe,MAAM,4BAA4B,CAAC;AACzD,OAAO,EACH,qBAAqB,EACrB,kCAAkC,EAClC,4BAA4B,EAC5B,mBAAmB,EACnB,2BAA2B,EAC3B,mBAAmB,EACnB,eAAe,EACf,sBAAsB,EACtB,2BAA2B,EAC3B,eAAe,GAClB,MAAM,4BAA4B,CAAC;AAEpC,yHAAyH;AACzH,MAAM,mBAAmB,GAAG;IACxB,WAAW,EAAE,MAAM;IACnB,wBAAwB,EAAE,MAAM;IAChC,WAAW,EAAE,MAAM;IACnB,YAAY,EAAE,MAAM;IACpB,uBAAuB,EAAE,MAAM;CAClC,CAAC;AAEF,MAAM,qBAAqB,GAAG,GAAiB,EAAE,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAE/E,MAAM,OAAO,iBAAiB;IAA9B;QACY,kBAAa,GAAyB,IAAI,CAAC;QAC3C,WAAM,GAAU,EAAE,CAAC;QACnB,kBAAa,GAA2B,qBAAqB,EAAE,CAAC;IAwM5E,CAAC;IAtMU,MAAM,CAAC,KAAK,CAAC,oBAAoB;;QACpC,IAAI,CAAA,MAAA,SAAS,aAAT,SAAS,uBAAT,SAAS,CAAE,YAAY,0CAAE,YAAY,KAAI,IAAI,IAAI,iBAAiB,CAAC,oBAAoB,EAAE,IAAI,IAAI,EAAE,CAAC;YACpG,OAAO,eAAe,EAAE,CAAC;QAC7B,CAAC;aAAM,CAAC;YACJ,OAAO,eAAe,EAAE,CAAC;QAC7B,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,cAAc,CAAC,OAA0B;QAClD,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,qBAAqB,EAAE,CAAC;QAClC,CAAC;QACD,MAAM,eAAe,GAAG,MAAM,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;QACvE,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,4BAA4B,EAAE,CAAC;QACzC,CAAC;QACD,MAAM,gBAAgB,GAAG,MAAM,iBAAiB,CAAC,2BAA2B,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9G,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;YAC1B,MAAM,sBAAsB,EAAE,CAAC;QACnC,CAAC;QAED,OAAO,SAAS,CAAC,YAAY;aACxB,YAAY,CAAC,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;aAC3B,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,8BAA8B,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;aACtE,KAAK,CAAC,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC;IAEM,KAAK,CAAC,aAAa;QACtB,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,2BAA2B,EAAE,CAAC;QACxC,CAAC;QACD,IAAI,CAAC;YACD,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACvE,OAAO,IAAI,CAAC,aAAa,CAAC;QAC9B,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YACd,MAAM,2BAA2B,EAAE,CAAC;QACxC,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,+BAA+B,EAAE,CAAC;QAC3C,CAAC;IACL,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,2BAA2B;QAC3C,sDAAsD;QACtD,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAC/B,IAAI,SAAS,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;gBACvC,OAAO,SAAS,CAAC,YAAY;qBACxB,YAAY,CAAC,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;qBAC3B,IAAI,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC;qBAC7B,KAAK,CAAC,GAAG,EAAE;oBACR,MAAM,kCAAkC,EAAE,CAAC;gBAC/C,CAAC,CAAC,CAAC;YACX,CAAC;QACL,CAAC;QACD,OAAO,SAAS,CAAC,WAAW;aACvB,KAAK,CAAC,EAAC,IAAI,EAAE,YAAmB,EAAC,CAAC;aAClC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAC,KAAK,EAAE,MAAM,CAAC,KAAK,KAAK,SAAS,EAAC,CAAC,CAAC;aACvD,KAAK,CAAC,GAAG,EAAE;YACR,MAAM,kCAAkC,EAAE,CAAC;QAC/C,CAAC,CAAC,CAAC;IACX,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,+BAA+B;QAC/C,MAAM,gBAAgB,GAAG,MAAM,iBAAiB,CAAC,2BAA2B,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9G,IAAI,gBAAgB,CAAC,KAAK,EAAE,CAAC;YACzB,OAAO,eAAe,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,SAAS,CAAC,YAAY;aACxB,YAAY,CAAC,EAAC,KAAK,EAAE,IAAI,EAAC,CAAC;aAC3B,IAAI,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC;aAC7B,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,EAAE,CAAC,CAAC;IACxC,CAAC;IAEM,cAAc;QACjB,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,2BAA2B,EAAE,CAAC;QACxC,CAAC;aAAM,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAClD,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;YAC3B,OAAO,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACJ,OAAO,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC;IAEM,eAAe;QAClB,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAC7B,MAAM,2BAA2B,EAAE,CAAC;QACxC,CAAC;aAAM,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC;YAC5B,OAAO,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,CAAC;aAAM,CAAC;YACJ,OAAO,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,CAAC;IACL,CAAC;IAEM,gBAAgB;QACnB,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,MAAM,EAAC,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAClD,OAAO,OAAO,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,WAAW,EAAC,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAC,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACJ,OAAO,OAAO,CAAC,OAAO,CAAC,EAAC,MAAM,EAAE,MAAM,EAAC,CAAC,CAAC;QAC7C,CAAC;IACL,CAAC;IAEM,MAAM,CAAC,oBAAoB;QAC9B,IAAI,CAAA,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,eAAe,KAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAExD,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,CAE/F,CAAC;QAEhB,OAAO,kBAAkB,aAAlB,kBAAkB,cAAlB,kBAAkB,GAAI,IAAI,CAAC;IACtC,CAAC;IAEO,8BAA8B,CAAC,MAAmB,EAAE,OAA0B;QAClF,IAAI,CAAC,aAAa,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjD,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,CAAC,aAAa,CAAC,OAAO,GAAG,GAAG,EAAE;gBAC9B,IAAI,CAAC,+BAA+B,EAAE,CAAC;gBACvC,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;YAClC,CAAC,CAAC;YACF,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,KAAK,IAAI,EAAE;;gBACnC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,oBAAoB,EAAE,CAAC;gBAC1D,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;oBACnB,IAAI,CAAC,+BAA+B,EAAE,CAAC;oBACvC,MAAM,CAAC,2BAA2B,EAAE,CAAC,CAAC;oBACtC,OAAO;gBACX,CAAC;gBACD,MAAM,kBAAkB,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC,CAAC,CAAC;gBACnE,IAAI,kBAAkB,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;oBAC/B,IAAI,CAAC,+BAA+B,EAAE,CAAC;oBACvC,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC;oBAC9B,OAAO;gBACX,CAAC;gBAED,IAAI,GAAG,GAAuB,SAAS,CAAC;gBACxC,IAAI,gBAAgB,GAAG,EAAE,CAAC;gBAC1B,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,EAAE,CAAC;oBACrB,MAAM,YAAY,GAAG,MAAA,MAAA,MAAA,OAAO,CAAC,YAAY,0CAAE,KAAK,CAAC,kBAAkB,CAAC,0CAAG,CAAC,CAAC,mCAAI,EAAE,CAAC;oBAChF,MAAM,IAAI,GAAG,GAAG,YAAY,cAAc,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,mBAAmB,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAEjG,MAAM,UAAU,CAAC;wBACb,IAAI,EAAE,kBAAkB;wBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;wBAC5B,SAAS,EAAE,IAAI;wBACf,IAAI;wBACJ,SAAS,EAAE,IAAI;qBAClB,CAAC,CAAC;oBAEH,CAAC,EAAC,GAAG,EAAC,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,EAAC,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC;gBAC5E,CAAC;qBAAM,CAAC;oBACJ,gBAAgB,GAAG,MAAM,iBAAiB,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC;gBAChF,CAAC;gBAED,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC,kBAAkB,CAAC,CAAC;gBACpE,IAAI,CAAC,+BAA+B,EAAE,CAAC;gBACvC,OAAO,CAAC,EAAC,KAAK,EAAE,EAAC,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB,GAAG,IAAI,EAAE,GAAG,EAAC,EAAC,CAAC,CAAC;YAC9F,CAAC,CAAC;YACF,IAAI,CAAC,aAAa,CAAC,eAAe,GAAG,CAAC,KAAU,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClF,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC/B,CAAC,CAAC,CAAC;QACH,OAAO,eAAe,EAAE,CAAC;IAC7B,CAAC;IAEO,wBAAwB;QAC5B,IAAI,CAAC,+BAA+B,EAAE,CAAC;QACvC,MAAM,mBAAmB,EAAE,CAAC;IAChC,CAAC;IAEO,MAAM,CAAC,YAAY,CAAC,IAAU;QAClC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC3B,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;YAChC,MAAM,CAAC,SAAS,GAAG,GAAG,EAAE;gBACpB,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAC9C,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACrD,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;gBAC5E,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9B,CAAC,CAAC;YACF,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,+BAA+B;QACnC,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YACzE,IAAI,CAAC;gBACD,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;YAC9B,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YAC5D,CAAC;QACL,CAAC;QACD,IAAI,CAAC,aAAa,GAAG,qBAAqB,EAAE,CAAC;QAC7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACrB,CAAC;CACJ","sourcesContent":["import {Filesystem} from '@capacitor/filesystem';\nimport write_blob from 'capacitor-blob-writer';\n\nimport type {\n Base64String,\n CurrentRecordingStatus,\n GenericResponse,\n RecordingData,\n RecordingOptions,\n} from './definitions';\nimport getBlobDuration from \"./helper/get-blob-duration\";\nimport {\n alreadyRecordingError,\n couldNotQueryPermissionStatusError,\n deviceCannotVoiceRecordError,\n emptyRecordingError,\n failedToFetchRecordingError,\n failedToRecordError,\n failureResponse,\n missingPermissionError,\n recordingHasNotStartedError,\n successResponse,\n} from './predefined-web-responses';\n\n// these mime types will be checked one by one in order until one of them is found to be supported by the current browser\nconst POSSIBLE_MIME_TYPES = {\n 'audio/aac': '.aac',\n 'audio/webm;codecs=opus': '.ogg',\n 'audio/mp4': '.mp3',\n 'audio/webm': '.ogg',\n 'audio/ogg;codecs=opus': '.ogg',\n};\n\nconst neverResolvingPromise = (): Promise<any> => new Promise(() => undefined);\n\nexport class VoiceRecorderImpl {\n private mediaRecorder: MediaRecorder | null = null;\n private chunks: any[] = [];\n private pendingResult: Promise<RecordingData> = neverResolvingPromise();\n\n public static async canDeviceVoiceRecord(): Promise<GenericResponse> {\n if (navigator?.mediaDevices?.getUserMedia == null || VoiceRecorderImpl.getSupportedMimeType() == null) {\n return failureResponse();\n } else {\n return successResponse();\n }\n }\n\n public async startRecording(options?: RecordingOptions): Promise<GenericResponse> {\n if (this.mediaRecorder != null) {\n throw alreadyRecordingError();\n }\n const deviceCanRecord = await VoiceRecorderImpl.canDeviceVoiceRecord();\n if (!deviceCanRecord.value) {\n throw deviceCannotVoiceRecordError();\n }\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => successResponse());\n if (!havingPermission.value) {\n throw missingPermissionError();\n }\n\n return navigator.mediaDevices\n .getUserMedia({audio: true})\n .then((stream) => this.onSuccessfullyStartedRecording(stream, options))\n .catch(this.onFailedToStartRecording.bind(this));\n }\n\n public async stopRecording(): Promise<RecordingData> {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n }\n try {\n this.mediaRecorder.stop();\n this.mediaRecorder.stream.getTracks().forEach((track) => track.stop());\n return this.pendingResult;\n } catch (ignore) {\n throw failedToFetchRecordingError();\n } finally {\n this.prepareInstanceForNextOperation();\n }\n }\n\n public static async hasAudioRecordingPermission(): Promise<GenericResponse> {\n // Safari does not support navigator.permissions.query\n if (!navigator.permissions.query) {\n if (navigator.mediaDevices !== undefined) {\n return navigator.mediaDevices\n .getUserMedia({audio: true})\n .then(() => successResponse())\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n }\n return navigator.permissions\n .query({name: 'microphone' as any})\n .then((result) => ({value: result.state === 'granted'}))\n .catch(() => {\n throw couldNotQueryPermissionStatusError();\n });\n }\n\n public static async requestAudioRecordingPermission(): Promise<GenericResponse> {\n const havingPermission = await VoiceRecorderImpl.hasAudioRecordingPermission().catch(() => failureResponse());\n if (havingPermission.value) {\n return successResponse();\n }\n\n return navigator.mediaDevices\n .getUserMedia({audio: true})\n .then(() => successResponse())\n .catch(() => failureResponse());\n }\n\n public pauseRecording(): Promise<GenericResponse> {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n } else if (this.mediaRecorder.state === 'recording') {\n this.mediaRecorder.pause();\n return Promise.resolve(successResponse());\n } else {\n return Promise.resolve(failureResponse());\n }\n }\n\n public resumeRecording(): Promise<GenericResponse> {\n if (this.mediaRecorder == null) {\n throw recordingHasNotStartedError();\n } else if (this.mediaRecorder.state === 'paused') {\n this.mediaRecorder.resume();\n return Promise.resolve(successResponse());\n } else {\n return Promise.resolve(failureResponse());\n }\n }\n\n public getCurrentStatus(): Promise<CurrentRecordingStatus> {\n if (this.mediaRecorder == null) {\n return Promise.resolve({status: 'NONE'});\n } else if (this.mediaRecorder.state === 'recording') {\n return Promise.resolve({status: 'RECORDING'});\n } else if (this.mediaRecorder.state === 'paused') {\n return Promise.resolve({status: 'PAUSED'});\n } else {\n return Promise.resolve({status: 'NONE'});\n }\n }\n\n public static getSupportedMimeType<T extends keyof typeof POSSIBLE_MIME_TYPES>(): T | null {\n if (MediaRecorder?.isTypeSupported == null) return null;\n\n const foundSupportedType = Object.keys(POSSIBLE_MIME_TYPES).find((type) => MediaRecorder.isTypeSupported(type)) as\n | T\n | undefined;\n\n return foundSupportedType ?? null;\n }\n\n private onSuccessfullyStartedRecording(stream: MediaStream, options?: RecordingOptions): GenericResponse {\n this.pendingResult = new Promise((resolve, reject) => {\n this.mediaRecorder = new MediaRecorder(stream);\n this.mediaRecorder.onerror = () => {\n this.prepareInstanceForNextOperation();\n reject(failedToRecordError());\n };\n this.mediaRecorder.onstop = async () => {\n const mimeType = VoiceRecorderImpl.getSupportedMimeType();\n if (mimeType == null) {\n this.prepareInstanceForNextOperation();\n reject(failedToFetchRecordingError());\n return;\n }\n const blobVoiceRecording = new Blob(this.chunks, {type: mimeType});\n if (blobVoiceRecording.size <= 0) {\n this.prepareInstanceForNextOperation();\n reject(emptyRecordingError());\n return;\n }\n\n let uri: string | undefined = undefined;\n let recordDataBase64 = '';\n if (options?.directory) {\n const subDirectory = options.subDirectory?.match(/^\\/?(.+[^/])\\/?$/)?.[1] ?? '';\n const path = `${subDirectory}/recording-${new Date().getTime()}${POSSIBLE_MIME_TYPES[mimeType]}`;\n\n await write_blob({\n blob: blobVoiceRecording,\n directory: options.directory,\n fast_mode: true,\n path,\n recursive: true,\n });\n\n ({uri} = await Filesystem.getUri({directory: options.directory, path}));\n } else {\n recordDataBase64 = await VoiceRecorderImpl.blobToBase64(blobVoiceRecording);\n }\n\n const recordingDuration = await getBlobDuration(blobVoiceRecording);\n this.prepareInstanceForNextOperation();\n resolve({value: {recordDataBase64, mimeType, msDuration: recordingDuration * 1000, uri}});\n };\n this.mediaRecorder.ondataavailable = (event: any) => this.chunks.push(event.data);\n this.mediaRecorder.start();\n });\n return successResponse();\n }\n\n private onFailedToStartRecording(): GenericResponse {\n this.prepareInstanceForNextOperation();\n throw failedToRecordError();\n }\n\n private static blobToBase64(blob: Blob): Promise<Base64String> {\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n const recordingResult = String(reader.result);\n const splitResult = recordingResult.split('base64,');\n const toResolve = splitResult.length > 1 ? splitResult[1] : recordingResult;\n resolve(toResolve.trim());\n };\n reader.readAsDataURL(blob);\n });\n }\n\n private prepareInstanceForNextOperation(): void {\n if (this.mediaRecorder != null && this.mediaRecorder.state === 'recording') {\n try {\n this.mediaRecorder.stop();\n } catch (ignore) {\n console.warn('Failed to stop recording during cleanup');\n }\n }\n this.pendingResult = neverResolvingPromise();\n this.mediaRecorder = null;\n this.chunks = [];\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"get-blob-duration.js","sourceRoot":"","sources":["../../../src/helper/get-blob-duration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,IAAmB;IACvD,MAAM,WAAW,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtD,WAAW,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,GAAG,EAAE;YAChD,2EAA2E;YAC3E,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACpC,WAAW,CAAC,WAAW,GAAG,MAAM,CAAC,gBAAgB,CAAC;gBAClD,WAAW,CAAC,YAAY,GAAG,GAAG,EAAE;oBAC5B,WAAW,CAAC,YAAY,GAAG,IAAI,CAAC;oBAChC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;oBAC9B,WAAW,CAAC,WAAW,GAAG,CAAC,CAAC;gBAChC,CAAC,CAAC;YACN,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YAClC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,WAAW,CAAC,OAAO,GAAG,CAAC,KAAK,EAAE,EAAE;YAC5B,MAAM,KAAK,GAAI,KAAoB,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACjF,MAAM,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC,CAAC;IACN,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,GAAG,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAE9E,OAAO,SAAS,CAAC;AACrB,CAAC","sourcesContent":["/**\n * @param {Blob | string} blob\n * @returns {Promise<number>} Blob duration in seconds.\n */\nexport default function getBlobDuration(blob: Blob | string): Promise<number> {\n const tempVideoEl = document.createElement('video');\n if (!tempVideoEl) throw new Error('Failed to create video element');\n const durationP = new Promise<number>((resolve, reject) => {\n tempVideoEl.addEventListener('loadedmetadata', () => {\n // Chrome bug: https://bugs.chromium.org/p/chromium/issues/detail?id=642012\n if (tempVideoEl.duration === Infinity) {\n tempVideoEl.currentTime = Number.MAX_SAFE_INTEGER;\n tempVideoEl.ontimeupdate = () => {\n tempVideoEl.ontimeupdate = null;\n resolve(tempVideoEl.duration);\n tempVideoEl.currentTime = 0;\n };\n } else {\n resolve(tempVideoEl.duration);\n }\n });\n\n tempVideoEl.onerror = (event) => {\n const error = (event as ErrorEvent).error || new Error('Unknown error occurred');\n reject(error);\n };\n });\n\n tempVideoEl.src = typeof blob === 'string' ? blob : URL.createObjectURL(blob);\n\n return durationP;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"predefined-web-responses.js","sourceRoot":"","sources":["../../src/predefined-web-responses.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,GAAoB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAoB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;AACnF,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;AACjF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;AACxF,MAAM,CAAC,MAAM,4BAA4B,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;AACjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;AAC9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAC/F,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAE/F,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAU,EAAE,CAAC,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC","sourcesContent":["import type { GenericResponse } from './definitions';\n\nexport const successResponse = (): GenericResponse => ({ value: true });\nexport const failureResponse = (): GenericResponse => ({ value: false });\n\nexport const missingPermissionError = (): Error => new Error('MISSING_PERMISSION');\nexport const alreadyRecordingError = (): Error => new Error('ALREADY_RECORDING');\nexport const microphoneBeingUsedError = (): Error => new Error('MICROPHONE_BEING_USED');\nexport const deviceCannotVoiceRecordError = (): Error => new Error('DEVICE_CANNOT_VOICE_RECORD');\nexport const failedToRecordError = (): Error => new Error('FAILED_TO_RECORD');\nexport const emptyRecordingError = (): Error => new Error('EMPTY_RECORDING');\n\nexport const recordingHasNotStartedError = (): Error => new Error('RECORDING_HAS_NOT_STARTED');\nexport const failedToFetchRecordingError = (): Error => new Error('FAILED_TO_FETCH_RECORDING');\n\nexport const couldNotQueryPermissionStatusError = (): Error => new Error('COULD_NOT_QUERY_PERMISSION_STATUS');\n"]}
@@ -1,8 +0,0 @@
1
- import Foundation
2
-
3
- struct RecordOptions {
4
-
5
- public let directory: String?
6
- public let subDirectory: String?
7
-
8
- }
@@ -1,170 +0,0 @@
1
- import Foundation
2
- import AVFoundation
3
- import Capacitor
4
-
5
- @objc(VoiceRecorder)
6
- public class VoiceRecorder: CAPPlugin, CAPBridgedPlugin {
7
- public let identifier = "VoiceRecorder"
8
- public let jsName = "VoiceRecorder"
9
- public let pluginMethods: [CAPPluginMethod] = [
10
- CAPPluginMethod(name: "canDeviceVoiceRecord", returnType: CAPPluginReturnPromise),
11
- CAPPluginMethod(name: "requestAudioRecordingPermission", returnType: CAPPluginReturnPromise),
12
- CAPPluginMethod(name: "hasAudioRecordingPermission", returnType: CAPPluginReturnPromise),
13
- CAPPluginMethod(name: "startRecording", returnType: CAPPluginReturnPromise),
14
- CAPPluginMethod(name: "stopRecording", returnType: CAPPluginReturnPromise),
15
- CAPPluginMethod(name: "pauseRecording", returnType: CAPPluginReturnPromise),
16
- CAPPluginMethod(name: "resumeRecording", returnType: CAPPluginReturnPromise),
17
- CAPPluginMethod(name: "getCurrentStatus", returnType: CAPPluginReturnPromise),
18
- ]
19
-
20
- private var customMediaRecorder: CustomMediaRecorder? = nil
21
-
22
- @objc func canDeviceVoiceRecord(_ call: CAPPluginCall) {
23
- call.resolve(ResponseGenerator.successResponse())
24
- }
25
-
26
- @objc func requestAudioRecordingPermission(_ call: CAPPluginCall) {
27
- AVAudioSession.sharedInstance().requestRecordPermission { granted in
28
- if granted {
29
- call.resolve(ResponseGenerator.successResponse())
30
- } else {
31
- call.resolve(ResponseGenerator.failResponse())
32
- }
33
- }
34
- }
35
-
36
- @objc func hasAudioRecordingPermission(_ call: CAPPluginCall) {
37
- call.resolve(ResponseGenerator.fromBoolean(doesUserGaveAudioRecordingPermission()))
38
- }
39
-
40
- @objc func startRecording(_ call: CAPPluginCall) {
41
- if(!doesUserGaveAudioRecordingPermission()) {
42
- call.reject(Messages.MISSING_PERMISSION)
43
- return
44
- }
45
-
46
- if(customMediaRecorder != nil) {
47
- call.reject(Messages.ALREADY_RECORDING)
48
- return
49
- }
50
-
51
- customMediaRecorder = CustomMediaRecorder()
52
- if(customMediaRecorder == nil) {
53
- call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE)
54
- return
55
- }
56
-
57
- customMediaRecorder?.onInterruptionBegan = { [weak self] in
58
- self?.notifyListeners("voiceRecordingInterrupted", data: [:])
59
- }
60
-
61
- customMediaRecorder?.onInterruptionEnded = { [weak self] in
62
- self?.notifyListeners("voiceRecordingInterruptionEnded", data: [:])
63
- }
64
-
65
- let directory: String? = call.getString("directory")
66
- let subDirectory: String? = call.getString("subDirectory")
67
- let recordOptions = RecordOptions(directory: directory, subDirectory: subDirectory)
68
- let successfullyStartedRecording = customMediaRecorder!.startRecording(recordOptions: recordOptions)
69
- if successfullyStartedRecording == false {
70
- call.reject(Messages.CANNOT_RECORD_ON_THIS_PHONE)
71
- } else {
72
- call.resolve(ResponseGenerator.successResponse())
73
- }
74
- }
75
-
76
- @objc func stopRecording(_ call: CAPPluginCall) {
77
- if(customMediaRecorder == nil) {
78
- call.reject(Messages.RECORDING_HAS_NOT_STARTED)
79
- return
80
- }
81
-
82
- customMediaRecorder?.stopRecording { [weak self] stopSuccess in
83
- DispatchQueue.main.async {
84
- guard let self = self else {
85
- call.reject(Messages.FAILED_TO_FETCH_RECORDING)
86
- return
87
- }
88
-
89
- if !stopSuccess {
90
- self.customMediaRecorder = nil
91
- call.reject(Messages.FAILED_TO_MERGE_RECORDING)
92
- return
93
- }
94
-
95
- let audioFileUrl = self.customMediaRecorder?.getOutputFile()
96
- if(audioFileUrl == nil) {
97
- self.customMediaRecorder = nil
98
- call.reject(Messages.FAILED_TO_FETCH_RECORDING)
99
- return
100
- }
101
-
102
- let fileExtension = audioFileUrl!.pathExtension.lowercased()
103
- let mimeType = fileExtension == "m4a" ? "audio/mp4" : "audio/aac"
104
- let sendDataAsBase64 = self.customMediaRecorder?.options?.directory == nil
105
- let recordData = RecordData(
106
- recordDataBase64: sendDataAsBase64 ? self.readFileAsBase64(audioFileUrl) : nil,
107
- mimeType: mimeType,
108
- msDuration: self.getMsDurationOfAudioFile(audioFileUrl),
109
- uri: sendDataAsBase64 ? nil : audioFileUrl!.path
110
- )
111
- self.customMediaRecorder = nil
112
- if (sendDataAsBase64 && recordData.recordDataBase64 == nil) || recordData.msDuration < 0 {
113
- call.reject(Messages.EMPTY_RECORDING)
114
- } else {
115
- call.resolve(ResponseGenerator.dataResponse(recordData.toDictionary()))
116
- }
117
- }
118
- }
119
- }
120
-
121
- @objc func pauseRecording(_ call: CAPPluginCall) {
122
- if(customMediaRecorder == nil) {
123
- call.reject(Messages.RECORDING_HAS_NOT_STARTED)
124
- } else {
125
- call.resolve(ResponseGenerator.fromBoolean(customMediaRecorder?.pauseRecording() ?? false))
126
- }
127
- }
128
-
129
- @objc func resumeRecording(_ call: CAPPluginCall) {
130
- if(customMediaRecorder == nil) {
131
- call.reject(Messages.RECORDING_HAS_NOT_STARTED)
132
- } else {
133
- call.resolve(ResponseGenerator.fromBoolean(customMediaRecorder?.resumeRecording() ?? false))
134
- }
135
- }
136
-
137
- @objc func getCurrentStatus(_ call: CAPPluginCall) {
138
- if(customMediaRecorder == nil) {
139
- call.resolve(ResponseGenerator.statusResponse(CurrentRecordingStatus.NONE))
140
- } else {
141
- call.resolve(ResponseGenerator.statusResponse(customMediaRecorder?.getCurrentStatus() ?? CurrentRecordingStatus.NONE))
142
- }
143
- }
144
-
145
- func doesUserGaveAudioRecordingPermission() -> Bool {
146
- return AVAudioSession.sharedInstance().recordPermission == AVAudioSession.RecordPermission.granted
147
- }
148
-
149
- func readFileAsBase64(_ filePath: URL?) -> String? {
150
- if(filePath == nil) {
151
- return nil
152
- }
153
-
154
- do {
155
- let fileData = try Data.init(contentsOf: filePath!)
156
- let fileStream = fileData.base64EncodedString(options: NSData.Base64EncodingOptions.init(rawValue: 0))
157
- return fileStream
158
- } catch {}
159
-
160
- return nil
161
- }
162
-
163
- func getMsDurationOfAudioFile(_ filePath: URL?) -> Int {
164
- if filePath == nil {
165
- return -1
166
- }
167
- return Int(CMTimeGetSeconds(AVURLAsset(url: filePath!).duration) * 1000)
168
- }
169
-
170
- }