nosnia-audio-recorder 0.8.2 → 0.8.4
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,19 +3,18 @@ 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
|
|
@@ -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,7 +433,7 @@ 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.
|
|
436
|
+
return "recording_$timestamp.m4a"
|
|
440
437
|
}
|
|
441
438
|
|
|
442
439
|
companion object {
|
|
@@ -189,11 +189,11 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
// Set audio category - use
|
|
193
|
-
//
|
|
192
|
+
// Set audio category - use PlayAndRecord for better simulator/device compatibility
|
|
193
|
+
// This works reliably on both real devices and simulators
|
|
194
194
|
NSError *categoryError = nil;
|
|
195
|
-
AVAudioSessionCategoryOptions options =
|
|
196
|
-
BOOL categorySuccess = [audioSession setCategory:
|
|
195
|
+
AVAudioSessionCategoryOptions options = AVAudioSessionCategoryOptionDefaultToSpeaker;
|
|
196
|
+
BOOL categorySuccess = [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord
|
|
197
197
|
withOptions:options
|
|
198
198
|
error:&categoryError];
|
|
199
199
|
if (!categorySuccess || categoryError) {
|
|
@@ -202,7 +202,7 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
202
202
|
reject(@"AUDIO_SESSION_ERROR", msg, categoryError);
|
|
203
203
|
return;
|
|
204
204
|
}
|
|
205
|
-
NSLog(@"[NosniaAudioRecorder] Audio category set to
|
|
205
|
+
NSLog(@"[NosniaAudioRecorder] Audio category set to PlayAndRecord with DefaultToSpeaker");
|
|
206
206
|
|
|
207
207
|
// Set audio mode to default for recording
|
|
208
208
|
NSError *modeError = nil;
|
|
@@ -317,14 +317,27 @@ RCT_EXPORT_METHOD(startRecording:(NSDictionary *)options
|
|
|
317
317
|
recordStarted = [_audioRecorder record];
|
|
318
318
|
if (!recordStarted) {
|
|
319
319
|
NSLog(@"[NosniaAudioRecorder] Failed to start recording. Recorder is recording: %d", _audioRecorder.recording);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
320
|
+
|
|
321
|
+
// Try alternative: reset audio session and retry
|
|
322
|
+
NSLog(@"[NosniaAudioRecorder] Attempting recovery: resetting audio session");
|
|
323
|
+
NSError *resetError = nil;
|
|
324
|
+
[[AVAudioSession sharedInstance] setActive:NO error:&resetError];
|
|
325
|
+
|
|
326
|
+
NSError *reactivateError = nil;
|
|
327
|
+
[[AVAudioSession sharedInstance] setActive:YES error:&reactivateError];
|
|
328
|
+
|
|
329
|
+
// Retry recording once
|
|
330
|
+
recordStarted = [_audioRecorder record];
|
|
331
|
+
if (!recordStarted) {
|
|
332
|
+
NSString *errorMsg = [NSString stringWithFormat:
|
|
333
|
+
@"Failed to start recording after retry. Recorder state: %ld. This may be a simulator audio limitation. Try on a real device.",
|
|
334
|
+
(long)_audioRecorder.recording];
|
|
335
|
+
NSLog(@"[NosniaAudioRecorder] %@", errorMsg);
|
|
336
|
+
reject(@"START_RECORDING_ERROR", errorMsg, nil);
|
|
337
|
+
_audioRecorder = nil;
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
NSLog(@"[NosniaAudioRecorder] Recovery successful - recording started");
|
|
328
341
|
}
|
|
329
342
|
NSLog(@"[NosniaAudioRecorder] Recording started successfully");
|
|
330
343
|
|
package/package.json
CHANGED