nosnia-audio-recorder 0.1.1 β 0.3.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/README.md +66 -3
- package/android/src/main/java/com/nosniaaudiorecorder/NosniaAudioPlayerModule.kt +238 -0
- package/android/src/main/java/com/nosniaaudiorecorder/NosniaAudioRecorderModule.kt +59 -1
- package/android/src/main/java/com/nosniaaudiorecorder/NosniaAudioRecorderPackage.kt +12 -4
- package/ios/NosniaAudioPlayer.h +7 -0
- package/ios/NosniaAudioPlayer.mm +263 -0
- package/ios/NosniaAudioRecorder.h +2 -1
- package/ios/NosniaAudioRecorder.mm +51 -0
- package/lib/module/AudioPlayer.js +176 -0
- package/lib/module/AudioPlayer.js.map +1 -0
- package/lib/module/NativeNosniaAudioPlayer.js +5 -0
- package/lib/module/NativeNosniaAudioPlayer.js.map +1 -0
- package/lib/module/index.js +37 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/AudioPlayer.d.ts +76 -0
- package/lib/typescript/src/AudioPlayer.d.ts.map +1 -0
- package/lib/typescript/src/NativeNosniaAudioPlayer.d.ts +24 -0
- package/lib/typescript/src/NativeNosniaAudioPlayer.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +17 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +19 -7
- package/src/AudioPlayer.tsx +201 -0
- package/src/NativeNosniaAudioPlayer.ts +26 -0
- package/src/index.tsx +58 -1
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ A compact, high-performance audio recorder library for React Native that records
|
|
|
8
8
|
- π± **Cross-Platform**: Works seamlessly on iOS and Android
|
|
9
9
|
- ποΈ **Configurable**: Adjust bitrate, sample rate, and channels
|
|
10
10
|
- βΈοΈ **Pause/Resume**: Full control over recording with pause and resume
|
|
11
|
+
- β±οΈ **Real-time Duration**: Get continuous recording duration updates via callback
|
|
12
|
+
- βΆοΈ **MP3 Playback**: Built-in audio player with play/pause/resume/stop/seek
|
|
11
13
|
- π **Permission Handling**: Built-in permission checking and requesting
|
|
12
14
|
- πΎ **File Management**: Automatic file naming and directory management
|
|
13
15
|
- π¦ **Compact**: Minimal dependencies, optimized for bundle size
|
|
@@ -48,10 +50,11 @@ const filePath = await NosniaAudioRecorder.stopRecording();
|
|
|
48
50
|
console.log('Recording saved to:', filePath);
|
|
49
51
|
```
|
|
50
52
|
|
|
51
|
-
### Full Example
|
|
53
|
+
### Full Example (Record + Playback)
|
|
52
54
|
|
|
53
55
|
```js
|
|
54
|
-
import { NosniaAudioRecorder } from 'nosnia-audio-recorder';
|
|
56
|
+
import { NosniaAudioRecorder, NosniaAudioPlayer } from 'nosnia-audio-recorder';
|
|
57
|
+
import { useState } from 'react';
|
|
55
58
|
|
|
56
59
|
// Check and request permission
|
|
57
60
|
const hasPermission = await NosniaAudioRecorder.checkPermission();
|
|
@@ -59,9 +62,16 @@ if (!hasPermission) {
|
|
|
59
62
|
await NosniaAudioRecorder.requestPermission();
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
// Start recording
|
|
65
|
+
// Start recording with real-time duration updates
|
|
63
66
|
await NosniaAudioRecorder.startRecording();
|
|
64
67
|
|
|
68
|
+
// Add listener for recording duration updates (every 100ms)
|
|
69
|
+
const removeListener = NosniaAudioRecorder.addRecordingProgressListener(
|
|
70
|
+
({ duration, isRecording }) => {
|
|
71
|
+
console.log(`Recording ${Math.floor(duration / 1000)}s`);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
65
75
|
// Pause if needed
|
|
66
76
|
await NosniaAudioRecorder.pauseRecording();
|
|
67
77
|
await NosniaAudioRecorder.resumeRecording();
|
|
@@ -73,8 +83,21 @@ console.log(`Recording: ${status.isRecording}, Duration: ${status.duration}ms`);
|
|
|
73
83
|
// Stop and save
|
|
74
84
|
const filePath = await NosniaAudioRecorder.stopRecording();
|
|
75
85
|
|
|
86
|
+
// Remove listener when done
|
|
87
|
+
removeListener();
|
|
88
|
+
|
|
76
89
|
// Or cancel (discard)
|
|
77
90
|
await NosniaAudioRecorder.cancelRecording();
|
|
91
|
+
|
|
92
|
+
// Play it back
|
|
93
|
+
await NosniaAudioPlayer.startPlaying({ filePath });
|
|
94
|
+
// Optionally listen to progress
|
|
95
|
+
const removePlayback = NosniaAudioPlayer.addPlaybackProgressListener(({ currentTime, duration }) => {
|
|
96
|
+
console.log(`Progress: ${Math.floor(currentTime / 1000)} / ${Math.floor(duration / 1000)}s`);
|
|
97
|
+
});
|
|
98
|
+
// Stop playback
|
|
99
|
+
await NosniaAudioPlayer.stopPlaying();
|
|
100
|
+
removePlayback();
|
|
78
101
|
```
|
|
79
102
|
|
|
80
103
|
## API Reference
|
|
@@ -89,6 +112,22 @@ await NosniaAudioRecorder.cancelRecording();
|
|
|
89
112
|
- `resumeRecording(): Promise<void>` - Resume recording
|
|
90
113
|
- `cancelRecording(): Promise<void>` - Cancel and discard
|
|
91
114
|
- `getStatus(): Promise<RecorderStatus>` - Get recorder status
|
|
115
|
+
- `addRecordingProgressListener(callback: RecordingProgressCallback): () => void` - Add listener for duration updates (returns cleanup function)
|
|
116
|
+
- `removeRecordingProgressListener(): void` - Remove duration listener
|
|
117
|
+
|
|
118
|
+
### Player Methods
|
|
119
|
+
|
|
120
|
+
- `startPlaying(options: PlayOptions): Promise<void>` - Start playing an audio file
|
|
121
|
+
- `stopPlaying(): Promise<void>` - Stop playback and reset
|
|
122
|
+
- `pausePlaying(): Promise<void>` - Pause playback
|
|
123
|
+
- `resumePlaying(): Promise<void>` - Resume playback
|
|
124
|
+
- `seekToTime(time: number): Promise<void>` - Seek to time (seconds)
|
|
125
|
+
- `setVolume(volume: number): Promise<void>` - Set playback volume (0.0β1.0)
|
|
126
|
+
- `getPlayerStatus(): Promise<PlayerStatus>` - Get playback status
|
|
127
|
+
- `addPlaybackProgressListener(callback: PlaybackProgressCallback): () => void` - Listen to playback progress
|
|
128
|
+
- `addPlaybackCompleteListener(callback: () => void): () => void` - Listen for playback completion
|
|
129
|
+
- `removePlaybackProgressListener(): void` - Remove progress listener
|
|
130
|
+
- `removePlaybackCompleteListener(): void` - Remove completion listener
|
|
92
131
|
|
|
93
132
|
### Types
|
|
94
133
|
|
|
@@ -105,6 +144,30 @@ interface RecorderStatus {
|
|
|
105
144
|
duration: number; // In milliseconds
|
|
106
145
|
currentFilePath?: string;
|
|
107
146
|
}
|
|
147
|
+
|
|
148
|
+
type RecordingProgressCallback = (data: {
|
|
149
|
+
duration: number; // In milliseconds
|
|
150
|
+
isRecording: boolean;
|
|
151
|
+
}) => void;
|
|
152
|
+
|
|
153
|
+
interface PlayOptions {
|
|
154
|
+
filePath: string;
|
|
155
|
+
volume?: number; // 0.0 - 1.0
|
|
156
|
+
loop?: boolean; // repeat playback
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
interface PlayerStatus {
|
|
160
|
+
isPlaying: boolean;
|
|
161
|
+
duration: number; // In milliseconds
|
|
162
|
+
currentTime: number; // In milliseconds
|
|
163
|
+
currentFilePath?: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type PlaybackProgressCallback = (data: {
|
|
167
|
+
currentTime: number;
|
|
168
|
+
duration: number;
|
|
169
|
+
isPlaying: boolean;
|
|
170
|
+
}) => void;
|
|
108
171
|
```
|
|
109
172
|
|
|
110
173
|
## Platform Setup
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
package com.nosniaaudiorecorder
|
|
2
|
+
|
|
3
|
+
import android.media.MediaPlayer
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import android.os.Looper
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReadableMap
|
|
9
|
+
import com.facebook.react.bridge.WritableMap
|
|
10
|
+
import com.facebook.react.bridge.WritableNativeMap
|
|
11
|
+
import com.facebook.react.bridge.Arguments
|
|
12
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
13
|
+
import com.facebook.react.module.annotations.ReactModule
|
|
14
|
+
import java.io.File
|
|
15
|
+
|
|
16
|
+
@ReactModule(name = NosniaAudioPlayerModule.NAME)
|
|
17
|
+
class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
18
|
+
NativeNosniaAudioPlayerSpec(reactContext) {
|
|
19
|
+
|
|
20
|
+
private var mediaPlayer: MediaPlayer? = null
|
|
21
|
+
private var currentFilePath: String? = null
|
|
22
|
+
private var isPlaying = false
|
|
23
|
+
|
|
24
|
+
private val progressHandler = Handler(Looper.getMainLooper())
|
|
25
|
+
private val progressRunnable = object : Runnable {
|
|
26
|
+
override fun run() {
|
|
27
|
+
mediaPlayer?.let { player ->
|
|
28
|
+
if (isPlaying && player.isPlaying) {
|
|
29
|
+
val currentTime = player.currentPosition.toLong()
|
|
30
|
+
val duration = player.duration.toLong()
|
|
31
|
+
|
|
32
|
+
sendEvent("onPlaybackProgress", Arguments.createMap().apply {
|
|
33
|
+
putDouble("currentTime", currentTime.toDouble())
|
|
34
|
+
putDouble("duration", duration.toDouble())
|
|
35
|
+
putBoolean("isPlaying", true)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
progressHandler.postDelayed(this, 100)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
45
|
+
reactApplicationContext
|
|
46
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
47
|
+
.emit(eventName, params)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun startProgressUpdates() {
|
|
51
|
+
progressHandler.post(progressRunnable)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private fun stopProgressUpdates() {
|
|
55
|
+
progressHandler.removeCallbacks(progressRunnable)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override fun getName(): String {
|
|
59
|
+
return NAME
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override fun startPlaying(options: ReadableMap, promise: Promise) {
|
|
63
|
+
try {
|
|
64
|
+
val filePath = options.getString("filePath")
|
|
65
|
+
if (filePath == null) {
|
|
66
|
+
promise.reject("INVALID_PATH", "File path is required")
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
val volume = if (options.hasKey("volume")) {
|
|
71
|
+
options.getDouble("volume").toFloat()
|
|
72
|
+
} else {
|
|
73
|
+
1.0f
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
val loop = if (options.hasKey("loop")) {
|
|
77
|
+
options.getBoolean("loop")
|
|
78
|
+
} else {
|
|
79
|
+
false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if file exists
|
|
83
|
+
val file = File(filePath)
|
|
84
|
+
if (!file.exists()) {
|
|
85
|
+
promise.reject("FILE_NOT_FOUND", "Audio file not found: $filePath")
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Release existing player if any
|
|
90
|
+
mediaPlayer?.release()
|
|
91
|
+
|
|
92
|
+
mediaPlayer = MediaPlayer().apply {
|
|
93
|
+
setDataSource(filePath)
|
|
94
|
+
setVolume(volume, volume)
|
|
95
|
+
isLooping = loop
|
|
96
|
+
|
|
97
|
+
setOnCompletionListener {
|
|
98
|
+
isPlaying = false
|
|
99
|
+
stopProgressUpdates()
|
|
100
|
+
sendEvent("onPlaybackComplete", Arguments.createMap())
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setOnErrorListener { _, what, extra ->
|
|
104
|
+
isPlaying = false
|
|
105
|
+
stopProgressUpdates()
|
|
106
|
+
true
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
prepare()
|
|
110
|
+
start()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
isPlaying = true
|
|
114
|
+
currentFilePath = filePath
|
|
115
|
+
startProgressUpdates()
|
|
116
|
+
promise.resolve(null)
|
|
117
|
+
} catch (e: Exception) {
|
|
118
|
+
mediaPlayer?.release()
|
|
119
|
+
mediaPlayer = null
|
|
120
|
+
isPlaying = false
|
|
121
|
+
promise.reject("START_PLAYING_ERROR", e.message, e)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override fun stopPlaying(promise: Promise) {
|
|
126
|
+
try {
|
|
127
|
+
stopProgressUpdates()
|
|
128
|
+
|
|
129
|
+
mediaPlayer?.apply {
|
|
130
|
+
if (isPlaying) {
|
|
131
|
+
stop()
|
|
132
|
+
}
|
|
133
|
+
release()
|
|
134
|
+
}
|
|
135
|
+
mediaPlayer = null
|
|
136
|
+
isPlaying = false
|
|
137
|
+
currentFilePath = null
|
|
138
|
+
|
|
139
|
+
promise.resolve(null)
|
|
140
|
+
} catch (e: Exception) {
|
|
141
|
+
promise.reject("STOP_PLAYING_ERROR", e.message, e)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
override fun pausePlaying(promise: Promise) {
|
|
146
|
+
try {
|
|
147
|
+
if (!isPlaying || mediaPlayer == null) {
|
|
148
|
+
promise.reject("NOT_PLAYING", "No playback in progress")
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
mediaPlayer?.pause()
|
|
153
|
+
isPlaying = false
|
|
154
|
+
promise.resolve(null)
|
|
155
|
+
} catch (e: Exception) {
|
|
156
|
+
promise.reject("PAUSE_ERROR", e.message, e)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
override fun resumePlaying(promise: Promise) {
|
|
161
|
+
try {
|
|
162
|
+
if (isPlaying || mediaPlayer == null) {
|
|
163
|
+
promise.reject("NOT_PAUSED", "Playback is not paused")
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
mediaPlayer?.start()
|
|
168
|
+
isPlaying = true
|
|
169
|
+
promise.resolve(null)
|
|
170
|
+
} catch (e: Exception) {
|
|
171
|
+
promise.reject("RESUME_ERROR", e.message, e)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
override fun seekToTime(time: Double, promise: Promise) {
|
|
176
|
+
try {
|
|
177
|
+
if (mediaPlayer == null) {
|
|
178
|
+
promise.reject("NO_PLAYER", "No audio player initialized")
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
val timeMs = (time * 1000).toInt()
|
|
183
|
+
mediaPlayer?.seekTo(timeMs)
|
|
184
|
+
promise.resolve(null)
|
|
185
|
+
} catch (e: Exception) {
|
|
186
|
+
promise.reject("SEEK_ERROR", e.message, e)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
override fun setVolume(volume: Double, promise: Promise) {
|
|
191
|
+
try {
|
|
192
|
+
if (mediaPlayer == null) {
|
|
193
|
+
promise.reject("NO_PLAYER", "No audio player initialized")
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
val volumeFloat = volume.toFloat()
|
|
198
|
+
mediaPlayer?.setVolume(volumeFloat, volumeFloat)
|
|
199
|
+
promise.resolve(null)
|
|
200
|
+
} catch (e: Exception) {
|
|
201
|
+
promise.reject("SET_VOLUME_ERROR", e.message, e)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
override fun getPlayerStatus(promise: Promise) {
|
|
206
|
+
try {
|
|
207
|
+
val status = WritableNativeMap().apply {
|
|
208
|
+
putBoolean("isPlaying", isPlaying)
|
|
209
|
+
|
|
210
|
+
if (mediaPlayer != null) {
|
|
211
|
+
putDouble("duration", mediaPlayer!!.duration.toDouble())
|
|
212
|
+
putDouble("currentTime", mediaPlayer!!.currentPosition.toDouble())
|
|
213
|
+
} else {
|
|
214
|
+
putDouble("duration", 0.0)
|
|
215
|
+
putDouble("currentTime", 0.0)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (currentFilePath != null) {
|
|
219
|
+
putString("currentFilePath", currentFilePath)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
promise.resolve(status)
|
|
223
|
+
} catch (e: Exception) {
|
|
224
|
+
promise.reject("STATUS_ERROR", e.message, e)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
override fun onCatalystInstanceDestroy() {
|
|
229
|
+
super.onCatalystInstanceDestroy()
|
|
230
|
+
stopProgressUpdates()
|
|
231
|
+
mediaPlayer?.release()
|
|
232
|
+
mediaPlayer = null
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
companion object {
|
|
236
|
+
const val NAME = "NosniaAudioPlayer"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -5,12 +5,16 @@ import android.content.Context
|
|
|
5
5
|
import android.media.MediaRecorder
|
|
6
6
|
import android.os.Build
|
|
7
7
|
import android.os.Environment
|
|
8
|
+
import android.os.Handler
|
|
9
|
+
import android.os.Looper
|
|
8
10
|
import androidx.core.content.ContextCompat
|
|
9
11
|
import com.facebook.react.bridge.Promise
|
|
10
12
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
11
13
|
import com.facebook.react.bridge.ReadableMap
|
|
12
14
|
import com.facebook.react.bridge.WritableMap
|
|
13
15
|
import com.facebook.react.bridge.WritableNativeMap
|
|
16
|
+
import com.facebook.react.bridge.Arguments
|
|
17
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
14
18
|
import com.facebook.react.module.annotations.ReactModule
|
|
15
19
|
import java.io.File
|
|
16
20
|
import java.text.SimpleDateFormat
|
|
@@ -25,6 +29,42 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
25
29
|
private var currentFilePath: String? = null
|
|
26
30
|
private var isRecording = false
|
|
27
31
|
private var isPaused = false
|
|
32
|
+
private var startTime: Long = 0
|
|
33
|
+
private var pausedDuration: Long = 0
|
|
34
|
+
private var pauseStartTime: Long = 0
|
|
35
|
+
|
|
36
|
+
private val progressHandler = Handler(Looper.getMainLooper())
|
|
37
|
+
private val progressRunnable = object : Runnable {
|
|
38
|
+
override fun run() {
|
|
39
|
+
if (isRecording && !isPaused) {
|
|
40
|
+
val currentTime = System.currentTimeMillis()
|
|
41
|
+
val duration = currentTime - startTime - pausedDuration
|
|
42
|
+
|
|
43
|
+
sendEvent("onRecordingProgress", Arguments.createMap().apply {
|
|
44
|
+
putDouble("duration", duration.toDouble())
|
|
45
|
+
putBoolean("isRecording", true)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
progressHandler.postDelayed(this, 100) // Update every 100ms
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
54
|
+
reactApplicationContext
|
|
55
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
56
|
+
.emit(eventName, params)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private fun startProgressUpdates() {
|
|
60
|
+
startTime = System.currentTimeMillis()
|
|
61
|
+
pausedDuration = 0
|
|
62
|
+
progressHandler.post(progressRunnable)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private fun stopProgressUpdates() {
|
|
66
|
+
progressHandler.removeCallbacks(progressRunnable)
|
|
67
|
+
}
|
|
28
68
|
|
|
29
69
|
override fun getName(): String {
|
|
30
70
|
return NAME
|
|
@@ -69,6 +109,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
69
109
|
|
|
70
110
|
isRecording = true
|
|
71
111
|
isPaused = false
|
|
112
|
+
startProgressUpdates()
|
|
72
113
|
promise.resolve(null)
|
|
73
114
|
} catch (e: Exception) {
|
|
74
115
|
mediaRecorder?.release()
|
|
@@ -85,6 +126,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
85
126
|
return
|
|
86
127
|
}
|
|
87
128
|
|
|
129
|
+
stopProgressUpdates()
|
|
88
130
|
mediaRecorder?.apply {
|
|
89
131
|
stop()
|
|
90
132
|
release()
|
|
@@ -97,6 +139,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
97
139
|
currentFilePath = null
|
|
98
140
|
promise.resolve(filePath)
|
|
99
141
|
} catch (e: Exception) {
|
|
142
|
+
stopProgressUpdates()
|
|
100
143
|
mediaRecorder?.release()
|
|
101
144
|
mediaRecorder = null
|
|
102
145
|
isRecording = false
|
|
@@ -114,6 +157,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
114
157
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
115
158
|
mediaRecorder?.pause()
|
|
116
159
|
isPaused = true
|
|
160
|
+
pauseStartTime = System.currentTimeMillis()
|
|
117
161
|
promise.resolve(null)
|
|
118
162
|
} else {
|
|
119
163
|
promise.reject("NOT_SUPPORTED", "Pause is not supported on this API level")
|
|
@@ -133,6 +177,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
133
177
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
134
178
|
mediaRecorder?.resume()
|
|
135
179
|
isPaused = false
|
|
180
|
+
pausedDuration += System.currentTimeMillis() - pauseStartTime
|
|
136
181
|
promise.resolve(null)
|
|
137
182
|
} else {
|
|
138
183
|
promise.reject("NOT_SUPPORTED", "Resume is not supported on this API level")
|
|
@@ -144,6 +189,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
144
189
|
|
|
145
190
|
override fun cancelRecording(promise: Promise) {
|
|
146
191
|
try {
|
|
192
|
+
stopProgressUpdates()
|
|
193
|
+
|
|
147
194
|
mediaRecorder?.apply {
|
|
148
195
|
try {
|
|
149
196
|
stop()
|
|
@@ -169,9 +216,20 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
169
216
|
|
|
170
217
|
override fun getRecorderStatus(promise: Promise) {
|
|
171
218
|
try {
|
|
219
|
+
val currentTime = System.currentTimeMillis()
|
|
220
|
+
val duration = if (isRecording) {
|
|
221
|
+
if (isPaused) {
|
|
222
|
+
pauseStartTime - startTime - pausedDuration
|
|
223
|
+
} else {
|
|
224
|
+
currentTime - startTime - pausedDuration
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
0
|
|
228
|
+
}
|
|
229
|
+
|
|
172
230
|
val status = WritableNativeMap().apply {
|
|
173
231
|
putBoolean("isRecording", isRecording)
|
|
174
|
-
putDouble("duration",
|
|
232
|
+
putDouble("duration", duration.toDouble())
|
|
175
233
|
if (currentFilePath != null) {
|
|
176
234
|
putString("currentFilePath", currentFilePath)
|
|
177
235
|
}
|
|
@@ -9,10 +9,10 @@ import java.util.HashMap
|
|
|
9
9
|
|
|
10
10
|
class NosniaAudioRecorderPackage : BaseReactPackage() {
|
|
11
11
|
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
-
return
|
|
13
|
-
NosniaAudioRecorderModule(reactContext)
|
|
14
|
-
|
|
15
|
-
null
|
|
12
|
+
return when (name) {
|
|
13
|
+
NosniaAudioRecorderModule.NAME -> NosniaAudioRecorderModule(reactContext)
|
|
14
|
+
NosniaAudioPlayerModule.NAME -> NosniaAudioPlayerModule(reactContext)
|
|
15
|
+
else -> null
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -27,6 +27,14 @@ class NosniaAudioRecorderPackage : BaseReactPackage() {
|
|
|
27
27
|
false, // isCxxModule
|
|
28
28
|
true // isTurboModule
|
|
29
29
|
)
|
|
30
|
+
moduleInfos[NosniaAudioPlayerModule.NAME] = ReactModuleInfo(
|
|
31
|
+
NosniaAudioPlayerModule.NAME,
|
|
32
|
+
NosniaAudioPlayerModule.NAME,
|
|
33
|
+
false, // canOverrideExistingModule
|
|
34
|
+
false, // needsEagerInit
|
|
35
|
+
false, // isCxxModule
|
|
36
|
+
true // isTurboModule
|
|
37
|
+
)
|
|
30
38
|
moduleInfos
|
|
31
39
|
}
|
|
32
40
|
}
|