nosnia-audio-recorder 0.8.3 → 0.8.5
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.
|
@@ -3,24 +3,23 @@ package com.nosniaaudiorecorder
|
|
|
3
3
|
import android.media.MediaPlayer
|
|
4
4
|
import android.os.Handler
|
|
5
5
|
import android.os.Looper
|
|
6
|
-
import com.facebook.react.bridge
|
|
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
|
|
6
|
+
import com.facebook.react.bridge.*
|
|
12
7
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
13
8
|
import com.facebook.react.module.annotations.ReactModule
|
|
14
9
|
import java.io.File
|
|
15
10
|
|
|
16
|
-
@ReactModule(name =
|
|
17
|
-
class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
18
|
-
|
|
11
|
+
@ReactModule(name = "NosniaAudioPlayer")
|
|
12
|
+
class NosniaAudioPlayerModule(private val reactContext: ReactApplicationContext) :
|
|
13
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
14
|
+
|
|
15
|
+
companion object {
|
|
16
|
+
const val NAME = "NosniaAudioPlayer"
|
|
17
|
+
}
|
|
19
18
|
|
|
20
19
|
private var mediaPlayer: MediaPlayer? = null
|
|
21
20
|
private var currentFilePath: String? = null
|
|
22
21
|
private var isPlaying = false
|
|
23
|
-
|
|
22
|
+
|
|
24
23
|
private val progressHandler = Handler(Looper.getMainLooper())
|
|
25
24
|
private val progressRunnable = object : Runnable {
|
|
26
25
|
override fun run() {
|
|
@@ -28,38 +27,37 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
28
27
|
if (isPlaying && player.isPlaying) {
|
|
29
28
|
val currentTime = player.currentPosition.toLong()
|
|
30
29
|
val duration = player.duration.toLong()
|
|
31
|
-
|
|
30
|
+
|
|
32
31
|
sendEvent("onPlaybackProgress", Arguments.createMap().apply {
|
|
33
32
|
putDouble("currentTime", currentTime.toDouble())
|
|
34
33
|
putDouble("duration", duration.toDouble())
|
|
35
34
|
putBoolean("isPlaying", true)
|
|
36
35
|
})
|
|
37
|
-
|
|
36
|
+
|
|
38
37
|
progressHandler.postDelayed(this, 100)
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
|
-
|
|
42
|
+
|
|
44
43
|
private fun sendEvent(eventName: String, params: WritableMap?) {
|
|
45
|
-
|
|
44
|
+
reactContext
|
|
46
45
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
47
46
|
.emit(eventName, params)
|
|
48
47
|
}
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
private fun startProgressUpdates() {
|
|
51
50
|
progressHandler.post(progressRunnable)
|
|
52
51
|
}
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
private fun stopProgressUpdates() {
|
|
55
54
|
progressHandler.removeCallbacks(progressRunnable)
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
override fun getName(): String
|
|
59
|
-
return NAME
|
|
60
|
-
}
|
|
57
|
+
override fun getName(): String = NAME
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
@ReactMethod
|
|
60
|
+
fun startPlaying(options: ReadableMap, promise: Promise) {
|
|
63
61
|
try {
|
|
64
62
|
val filePath = options.getString("filePath")
|
|
65
63
|
if (filePath == null) {
|
|
@@ -79,33 +77,31 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
79
77
|
false
|
|
80
78
|
}
|
|
81
79
|
|
|
82
|
-
// Check if file exists
|
|
83
80
|
val file = File(filePath)
|
|
84
81
|
if (!file.exists()) {
|
|
85
82
|
promise.reject("FILE_NOT_FOUND", "Audio file not found: $filePath")
|
|
86
83
|
return
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
// Release existing player if any
|
|
90
86
|
mediaPlayer?.release()
|
|
91
87
|
|
|
92
88
|
mediaPlayer = MediaPlayer().apply {
|
|
93
89
|
setDataSource(filePath)
|
|
94
90
|
setVolume(volume, volume)
|
|
95
91
|
isLooping = loop
|
|
96
|
-
|
|
92
|
+
|
|
97
93
|
setOnCompletionListener {
|
|
98
94
|
isPlaying = false
|
|
99
95
|
stopProgressUpdates()
|
|
100
96
|
sendEvent("onPlaybackComplete", Arguments.createMap())
|
|
101
97
|
}
|
|
102
|
-
|
|
103
|
-
setOnErrorListener { _,
|
|
98
|
+
|
|
99
|
+
setOnErrorListener { _, _, _ ->
|
|
104
100
|
isPlaying = false
|
|
105
101
|
stopProgressUpdates()
|
|
106
102
|
true
|
|
107
103
|
}
|
|
108
|
-
|
|
104
|
+
|
|
109
105
|
prepare()
|
|
110
106
|
start()
|
|
111
107
|
}
|
|
@@ -122,10 +118,11 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
122
118
|
}
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
|
|
121
|
+
@ReactMethod
|
|
122
|
+
fun stopPlaying(promise: Promise) {
|
|
126
123
|
try {
|
|
127
124
|
stopProgressUpdates()
|
|
128
|
-
|
|
125
|
+
|
|
129
126
|
mediaPlayer?.apply {
|
|
130
127
|
if (isPlaying) {
|
|
131
128
|
stop()
|
|
@@ -135,14 +132,15 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
135
132
|
mediaPlayer = null
|
|
136
133
|
isPlaying = false
|
|
137
134
|
currentFilePath = null
|
|
138
|
-
|
|
135
|
+
|
|
139
136
|
promise.resolve(null)
|
|
140
137
|
} catch (e: Exception) {
|
|
141
138
|
promise.reject("STOP_PLAYING_ERROR", e.message, e)
|
|
142
139
|
}
|
|
143
140
|
}
|
|
144
141
|
|
|
145
|
-
|
|
142
|
+
@ReactMethod
|
|
143
|
+
fun pausePlaying(promise: Promise) {
|
|
146
144
|
try {
|
|
147
145
|
if (!isPlaying || mediaPlayer == null) {
|
|
148
146
|
promise.reject("NOT_PLAYING", "No playback in progress")
|
|
@@ -157,7 +155,8 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
157
155
|
}
|
|
158
156
|
}
|
|
159
157
|
|
|
160
|
-
|
|
158
|
+
@ReactMethod
|
|
159
|
+
fun resumePlaying(promise: Promise) {
|
|
161
160
|
try {
|
|
162
161
|
if (isPlaying || mediaPlayer == null) {
|
|
163
162
|
promise.reject("NOT_PAUSED", "Playback is not paused")
|
|
@@ -172,7 +171,8 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
172
171
|
}
|
|
173
172
|
}
|
|
174
173
|
|
|
175
|
-
|
|
174
|
+
@ReactMethod
|
|
175
|
+
fun seekToTime(time: Double, promise: Promise) {
|
|
176
176
|
try {
|
|
177
177
|
if (mediaPlayer == null) {
|
|
178
178
|
promise.reject("NO_PLAYER", "No audio player initialized")
|
|
@@ -187,7 +187,8 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
@ReactMethod
|
|
191
|
+
fun setVolume(volume: Double, promise: Promise) {
|
|
191
192
|
try {
|
|
192
193
|
if (mediaPlayer == null) {
|
|
193
194
|
promise.reject("NO_PLAYER", "No audio player initialized")
|
|
@@ -202,11 +203,12 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
202
203
|
}
|
|
203
204
|
}
|
|
204
205
|
|
|
205
|
-
|
|
206
|
+
@ReactMethod
|
|
207
|
+
fun getPlayerStatus(promise: Promise) {
|
|
206
208
|
try {
|
|
207
209
|
val status = WritableNativeMap().apply {
|
|
208
210
|
putBoolean("isPlaying", isPlaying)
|
|
209
|
-
|
|
211
|
+
|
|
210
212
|
if (mediaPlayer != null) {
|
|
211
213
|
putDouble("duration", mediaPlayer!!.duration.toDouble())
|
|
212
214
|
putDouble("currentTime", mediaPlayer!!.currentPosition.toDouble())
|
|
@@ -214,7 +216,7 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
214
216
|
putDouble("duration", 0.0)
|
|
215
217
|
putDouble("currentTime", 0.0)
|
|
216
218
|
}
|
|
217
|
-
|
|
219
|
+
|
|
218
220
|
if (currentFilePath != null) {
|
|
219
221
|
putString("currentFilePath", currentFilePath)
|
|
220
222
|
}
|
|
@@ -231,8 +233,4 @@ class NosniaAudioPlayerModule(reactContext: ReactApplicationContext) :
|
|
|
231
233
|
mediaPlayer?.release()
|
|
232
234
|
mediaPlayer = null
|
|
233
235
|
}
|
|
234
|
-
|
|
235
|
-
companion object {
|
|
236
|
-
const val NAME = "NosniaAudioPlayer"
|
|
237
|
-
}
|
|
238
236
|
}
|
|
@@ -8,12 +8,7 @@ import android.os.Environment
|
|
|
8
8
|
import android.os.Handler
|
|
9
9
|
import android.os.Looper
|
|
10
10
|
import androidx.core.content.ContextCompat
|
|
11
|
-
import com.facebook.react.bridge
|
|
12
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
|
-
import com.facebook.react.bridge.ReadableMap
|
|
14
|
-
import com.facebook.react.bridge.WritableMap
|
|
15
|
-
import com.facebook.react.bridge.WritableNativeMap
|
|
16
|
-
import com.facebook.react.bridge.Arguments
|
|
11
|
+
import com.facebook.react.bridge.*
|
|
17
12
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
18
13
|
import com.facebook.react.module.annotations.ReactModule
|
|
19
14
|
import java.io.File
|
|
@@ -21,9 +16,13 @@ import java.text.SimpleDateFormat
|
|
|
21
16
|
import java.util.Date
|
|
22
17
|
import java.util.Locale
|
|
23
18
|
|
|
24
|
-
@ReactModule(name =
|
|
25
|
-
class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
26
|
-
|
|
19
|
+
@ReactModule(name = "NosniaAudioRecorder")
|
|
20
|
+
class NosniaAudioRecorderModule(private val reactContext: ReactApplicationContext) :
|
|
21
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
22
|
+
|
|
23
|
+
companion object {
|
|
24
|
+
const val NAME = "NosniaAudioRecorder"
|
|
25
|
+
}
|
|
27
26
|
|
|
28
27
|
private var mediaRecorder: MediaRecorder? = null
|
|
29
28
|
private var currentFilePath: String? = null
|
|
@@ -66,11 +65,10 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
66
65
|
progressHandler.removeCallbacks(progressRunnable)
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
override fun getName(): String
|
|
70
|
-
return NAME
|
|
71
|
-
}
|
|
68
|
+
override fun getName(): String = NAME
|
|
72
69
|
|
|
73
|
-
|
|
70
|
+
@ReactMethod
|
|
71
|
+
fun startRecording(options: ReadableMap, promise: Promise) {
|
|
74
72
|
try {
|
|
75
73
|
// Prevent concurrent recordings
|
|
76
74
|
if (isRecording) {
|
|
@@ -189,7 +187,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
189
187
|
}
|
|
190
188
|
}
|
|
191
189
|
|
|
192
|
-
|
|
190
|
+
@ReactMethod
|
|
191
|
+
fun stopRecording(promise: Promise) {
|
|
193
192
|
try {
|
|
194
193
|
if (!isRecording || mediaRecorder == null) {
|
|
195
194
|
promise.reject("NOT_RECORDING", "No recording in progress")
|
|
@@ -246,7 +245,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
246
245
|
}
|
|
247
246
|
}
|
|
248
247
|
|
|
249
|
-
|
|
248
|
+
@ReactMethod
|
|
249
|
+
fun pauseRecording(promise: Promise) {
|
|
250
250
|
try {
|
|
251
251
|
if (!isRecording || mediaRecorder == null) {
|
|
252
252
|
promise.reject("NOT_RECORDING", "No recording in progress")
|
|
@@ -275,7 +275,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
|
|
278
|
+
@ReactMethod
|
|
279
|
+
fun resumeRecording(promise: Promise) {
|
|
279
280
|
try {
|
|
280
281
|
if (!isRecording || mediaRecorder == null) {
|
|
281
282
|
promise.reject("NOT_RECORDING", "No recording in progress")
|
|
@@ -304,7 +305,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
304
305
|
}
|
|
305
306
|
}
|
|
306
307
|
|
|
307
|
-
|
|
308
|
+
@ReactMethod
|
|
309
|
+
fun cancelRecording(promise: Promise) {
|
|
308
310
|
try {
|
|
309
311
|
stopProgressUpdates()
|
|
310
312
|
|
|
@@ -353,7 +355,8 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
353
355
|
}
|
|
354
356
|
}
|
|
355
357
|
|
|
356
|
-
|
|
358
|
+
@ReactMethod
|
|
359
|
+
fun getRecorderStatus(promise: Promise) {
|
|
357
360
|
try {
|
|
358
361
|
val currentTime = System.currentTimeMillis()
|
|
359
362
|
|
|
@@ -385,20 +388,13 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
385
388
|
promise.reject("STATUS_ERROR", e.message ?: "Failed to get recorder status", e)
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
|
-
putString("currentFilePath", currentFilePath)
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
promise.resolve(status)
|
|
392
|
-
} catch (e: Exception) {
|
|
393
|
-
promise.reject("STATUS_ERROR", e.message, e)
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
391
|
|
|
397
|
-
|
|
392
|
+
@ReactMethod
|
|
393
|
+
fun requestAudioPermission(promise: Promise) {
|
|
398
394
|
try {
|
|
399
395
|
val permission = Manifest.permission.RECORD_AUDIO
|
|
400
396
|
val hasPermission = ContextCompat.checkSelfPermission(
|
|
401
|
-
|
|
397
|
+
reactContext,
|
|
402
398
|
permission
|
|
403
399
|
) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
404
400
|
|
|
@@ -408,11 +404,12 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
408
404
|
}
|
|
409
405
|
}
|
|
410
406
|
|
|
411
|
-
|
|
407
|
+
@ReactMethod
|
|
408
|
+
fun checkAudioPermission(promise: Promise) {
|
|
412
409
|
try {
|
|
413
410
|
val permission = Manifest.permission.RECORD_AUDIO
|
|
414
411
|
val hasPermission = ContextCompat.checkSelfPermission(
|
|
415
|
-
|
|
412
|
+
reactContext,
|
|
416
413
|
permission
|
|
417
414
|
) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
418
415
|
|
|
@@ -424,7 +421,7 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
424
421
|
|
|
425
422
|
private fun getRecordingDirectory(): File {
|
|
426
423
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
427
|
-
File(
|
|
424
|
+
File(reactContext.getExternalFilesDir(null), "recordings")
|
|
428
425
|
} else {
|
|
429
426
|
@Suppress("DEPRECATION")
|
|
430
427
|
File(
|
|
@@ -436,10 +433,6 @@ class NosniaAudioRecorderModule(reactContext: ReactApplicationContext) :
|
|
|
436
433
|
|
|
437
434
|
private fun generateFilename(): String {
|
|
438
435
|
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
|
|
439
|
-
return "recording_$timestamp.
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
companion object {
|
|
443
|
-
const val NAME = "NosniaAudioRecorder"
|
|
436
|
+
return "recording_$timestamp.m4a"
|
|
444
437
|
}
|
|
445
438
|
}
|
package/package.json
CHANGED