@siteed/expo-audio-studio 2.2.0 → 2.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/CHANGELOG.md +7 -5
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/net/siteed/audiostream/AudioRecorderManager.kt +8 -4
- package/android/src/main/java/net/siteed/audiostream/ExpoAudioStreamModule.kt +23 -16
- package/ios/AudioProcessor.swift +7 -4
- package/ios/AudioStreamManager.swift +25 -11
- package/ios/ExpoAudioStreamModule.swift +6 -4
- package/package.json +1 -1
- package/plugin/build/index.js +2 -3
- package/plugin/src/index.ts +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -8,15 +8,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [2.3.0] - 2025-03-29
|
|
12
|
+
### Changed
|
|
13
|
+
- fix: always generate a new UUID unless filename is provided (#182) ([f98a9a5](https://github.com/deeeed/expo-audio-stream/commit/f98a9a52393829e6c4a79aee3575fbfcc9416c19))
|
|
14
|
+
- refactor(audio-studio): introduce constants for silence threshold and WAV header size (#188) ([e8aa329](https://github.com/deeeed/expo-audio-stream/commit/e8aa3298bd6ba029d38898360b7df26b3fd5485f))
|
|
15
|
+
- docs: enhance installation and API reference documentation for phone call handling (#187) ([fcaece1](https://github.com/deeeed/expo-audio-stream/commit/fcaece18cf046d970b9659f3f12a19deb096bceb))
|
|
11
16
|
## [2.2.0] - 2025-03-28
|
|
12
17
|
### Changed
|
|
13
18
|
- refactor(audio-studio): implement platform-specific CRC32 handling ([b61a3d7](https://github.com/deeeed/expo-audio-stream/commit/b61a3d743914e66888ec6cc4cb8e010ff1992698))
|
|
14
19
|
- chore: update Expo dependencies and remove invalid design-system version ([16e5007](https://github.com/deeeed/expo-audio-stream/commit/16e50077690b55977c22fbcb08be75834146ff47))
|
|
15
20
|
- fix: linting issues ([741589d](https://github.com/deeeed/expo-audio-stream/commit/741589d60485a2d049e7adf529d3fd2b999fa098))
|
|
16
|
-
- feat: Enhance Audio Processing and Transcription Capabilities in Playground App (#171) ([1ec6026](https://github.com/deeeed/expo-audio-stream/commit/1ec6026ff75fc3ff7122b5df72e8dcd15ce848bd))
|
|
17
|
-
- feat: Enhance Essentia Integration with iOS and Android Build Improvements (#169) ([422fd50](https://github.com/deeeed/expo-audio-stream/commit/422fd501a5ec71f30df660d56559bc410084b797))
|
|
18
|
-
- feat: Enhance Audio Analysis with Mel Spectrogram Comparison and Pipeline Support (#164) ([541e13c](https://github.com/deeeed/expo-audio-stream/commit/541e13c6e01b8ff9947bc69dc7c29ffed6d8ee07))
|
|
19
|
-
- feat: Integrate Essentia Audio Analysis Library into React Native for Android (#163) ([4cac310](https://github.com/deeeed/expo-audio-stream/commit/4cac310e4af47ddda528dee0f2840e3a336c6823))
|
|
20
21
|
- chore(expo-audio-studio): release @siteed/expo-audio-studio@2.1.1 ([1b17ac6](https://github.com/deeeed/expo-audio-stream/commit/1b17ac6e103f2ca50f29668b3ddaaf57a4b4b7d3))
|
|
21
22
|
## [2.1.1] - 2025-03-04
|
|
22
23
|
### Changed
|
|
@@ -181,7 +182,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
181
182
|
- Feature: Audio features extraction during recording.
|
|
182
183
|
- Feature: Consistent WAV PCM recording format across all platforms.
|
|
183
184
|
|
|
184
|
-
[unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.
|
|
185
|
+
[unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.3.0...HEAD
|
|
186
|
+
[2.3.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.2.0...@siteed/expo-audio-studio@2.3.0
|
|
185
187
|
[2.2.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.1.1...@siteed/expo-audio-studio@2.2.0
|
|
186
188
|
[2.1.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-studio@2.1.0...@siteed/expo-audio-studio@2.1.1
|
|
187
189
|
[2.1.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@2.0.1...@siteed/expo-audio-stream@2.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
1
2
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
3
|
<!-- Permissions will be merged into the app's manifest -->
|
|
3
|
-
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
|
4
4
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
|
5
5
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
6
6
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
|
|
@@ -37,7 +37,8 @@ class AudioRecorderManager(
|
|
|
37
37
|
private val filesDir: File,
|
|
38
38
|
private val permissionUtils: PermissionUtils,
|
|
39
39
|
private val audioDataEncoder: AudioDataEncoder,
|
|
40
|
-
private val eventSender: EventSender
|
|
40
|
+
private val eventSender: EventSender,
|
|
41
|
+
private val enablePhoneStateHandling: Boolean = true
|
|
41
42
|
) {
|
|
42
43
|
private var audioRecord: AudioRecord? = null
|
|
43
44
|
private var bufferSizeInBytes = 0
|
|
@@ -348,8 +349,10 @@ class AudioRecorderManager(
|
|
|
348
349
|
@RequiresApi(Build.VERSION_CODES.R)
|
|
349
350
|
fun startRecording(options: Map<String, Any?>, promise: Promise) {
|
|
350
351
|
try {
|
|
351
|
-
// Initialize phone state listener
|
|
352
|
-
|
|
352
|
+
// Initialize phone state listener only if enabled
|
|
353
|
+
if (enablePhoneStateHandling) {
|
|
354
|
+
initializePhoneStateListener()
|
|
355
|
+
}
|
|
353
356
|
|
|
354
357
|
// Request audio focus
|
|
355
358
|
if (!requestAudioFocus()) {
|
|
@@ -479,7 +482,8 @@ class AudioRecorderManager(
|
|
|
479
482
|
return false
|
|
480
483
|
}
|
|
481
484
|
|
|
482
|
-
if
|
|
485
|
+
// Only check phone state permission if enabled
|
|
486
|
+
if (enablePhoneStateHandling && !permissionUtils.checkPhoneStatePermission()) {
|
|
483
487
|
Log.w(Constants.TAG, "READ_PHONE_STATE permission not granted, phone call interruption handling will be disabled")
|
|
484
488
|
// Don't reject here, just log warning as this is optional
|
|
485
489
|
}
|
|
@@ -16,6 +16,7 @@ import java.util.zip.CRC32
|
|
|
16
16
|
class ExpoAudioStreamModule : Module(), EventSender {
|
|
17
17
|
private lateinit var audioRecorderManager: AudioRecorderManager
|
|
18
18
|
private lateinit var audioProcessor: AudioProcessor
|
|
19
|
+
private var enablePhoneStateHandling: Boolean = true // Default to true for backward compatibility
|
|
19
20
|
|
|
20
21
|
private val audioFileHandler by lazy {
|
|
21
22
|
AudioFileHandler(appContext.reactContext?.filesDir ?: throw IllegalStateException("React context not available"))
|
|
@@ -174,10 +175,14 @@ class ExpoAudioStreamModule : Module(), EventSender {
|
|
|
174
175
|
AsyncFunction("requestPermissionsAsync") { promise: Promise ->
|
|
175
176
|
try {
|
|
176
177
|
val permissions = mutableListOf(
|
|
177
|
-
Manifest.permission.RECORD_AUDIO
|
|
178
|
-
Manifest.permission.READ_PHONE_STATE
|
|
178
|
+
Manifest.permission.RECORD_AUDIO
|
|
179
179
|
)
|
|
180
180
|
|
|
181
|
+
// Only add phone state permission if enabled
|
|
182
|
+
if (enablePhoneStateHandling) {
|
|
183
|
+
permissions.add(Manifest.permission.READ_PHONE_STATE)
|
|
184
|
+
}
|
|
185
|
+
|
|
181
186
|
// Add foreground service permission for Android 14+
|
|
182
187
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
183
188
|
Log.d(Constants.TAG, "Adding FOREGROUND_SERVICE_MICROPHONE permission request")
|
|
@@ -198,10 +203,14 @@ class ExpoAudioStreamModule : Module(), EventSender {
|
|
|
198
203
|
|
|
199
204
|
AsyncFunction("getPermissionsAsync") { promise: Promise ->
|
|
200
205
|
val permissions = mutableListOf(
|
|
201
|
-
Manifest.permission.RECORD_AUDIO
|
|
202
|
-
Manifest.permission.READ_PHONE_STATE
|
|
206
|
+
Manifest.permission.RECORD_AUDIO
|
|
203
207
|
)
|
|
204
208
|
|
|
209
|
+
// Only add phone state permission if enabled
|
|
210
|
+
if (enablePhoneStateHandling) {
|
|
211
|
+
permissions.add(Manifest.permission.READ_PHONE_STATE)
|
|
212
|
+
}
|
|
213
|
+
|
|
205
214
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
206
215
|
permissions.add(Manifest.permission.FOREGROUND_SERVICE_MICROPHONE)
|
|
207
216
|
}
|
|
@@ -714,20 +723,18 @@ class ExpoAudioStreamModule : Module(), EventSender {
|
|
|
714
723
|
}
|
|
715
724
|
|
|
716
725
|
private fun initializeManager() {
|
|
717
|
-
val
|
|
718
|
-
|
|
719
|
-
val
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
audioRecorderManager = AudioRecorderManager.initialize(
|
|
724
|
-
androidContext,
|
|
725
|
-
androidContext.filesDir,
|
|
726
|
+
val filesDir = appContext.reactContext?.filesDir ?: throw IllegalStateException("React context not available")
|
|
727
|
+
val permissionUtils = PermissionUtils(appContext.reactContext!!)
|
|
728
|
+
val audioDataEncoder = AudioDataEncoder()
|
|
729
|
+
audioRecorderManager = AudioRecorderManager(
|
|
730
|
+
appContext.reactContext!!,
|
|
731
|
+
filesDir,
|
|
726
732
|
permissionUtils,
|
|
727
|
-
|
|
728
|
-
this
|
|
733
|
+
audioDataEncoder,
|
|
734
|
+
this,
|
|
735
|
+
enablePhoneStateHandling
|
|
729
736
|
)
|
|
730
|
-
audioProcessor = AudioProcessor(
|
|
737
|
+
audioProcessor = AudioProcessor(filesDir)
|
|
731
738
|
}
|
|
732
739
|
|
|
733
740
|
|
package/ios/AudioProcessor.swift
CHANGED
|
@@ -5,6 +5,9 @@ import Accelerate
|
|
|
5
5
|
import AVFoundation
|
|
6
6
|
import QuartzCore
|
|
7
7
|
|
|
8
|
+
// Constants
|
|
9
|
+
private let SILENCE_THRESHOLD_RMS: Float = 0.01
|
|
10
|
+
|
|
8
11
|
public struct TrimResult {
|
|
9
12
|
let uri: String
|
|
10
13
|
let filename: String
|
|
@@ -416,7 +419,7 @@ public class AudioProcessor {
|
|
|
416
419
|
) -> DataPoint {
|
|
417
420
|
let sumSquares: Float = segment.reduce(0) { $0 + $1 * $1 }
|
|
418
421
|
let rms = sqrt(sumSquares / Float(segment.count))
|
|
419
|
-
let silent = rms <
|
|
422
|
+
let silent = rms < SILENCE_THRESHOLD_RMS
|
|
420
423
|
let dB = Float(20 * log10(Double(rms)))
|
|
421
424
|
|
|
422
425
|
let features = computeFeatures(
|
|
@@ -590,7 +593,7 @@ public class AudioProcessor {
|
|
|
590
593
|
amplitude: localMax, // Always use peak amplitude
|
|
591
594
|
rms: rms, // Use calculated RMS value
|
|
592
595
|
dB: Float(20 * log10(Double(rms))), // Use RMS for dB calculation
|
|
593
|
-
silent: rms <
|
|
596
|
+
silent: rms < SILENCE_THRESHOLD_RMS, // Use RMS for silence detection
|
|
594
597
|
features: computeFeatures(
|
|
595
598
|
segmentData: Array(UnsafeBufferPointer(start: summedData, count: Int(framesToRead))),
|
|
596
599
|
sampleRate: sampleRate,
|
|
@@ -599,7 +602,7 @@ public class AudioProcessor {
|
|
|
599
602
|
segmentLength: Int(framesToRead),
|
|
600
603
|
featureOptions: featureOptions
|
|
601
604
|
),
|
|
602
|
-
speech: SpeechFeatures(isActive: rms >=
|
|
605
|
+
speech: SpeechFeatures(isActive: rms >= SILENCE_THRESHOLD_RMS),
|
|
603
606
|
startTime: startTime,
|
|
604
607
|
endTime: endTime,
|
|
605
608
|
startPosition: Int(currentFrame),
|
|
@@ -1229,7 +1232,7 @@ public class AudioProcessor {
|
|
|
1229
1232
|
featureOptions: featureOptions)
|
|
1230
1233
|
|
|
1231
1234
|
let rms = features.rms
|
|
1232
|
-
let silent = rms <
|
|
1235
|
+
let silent = rms < SILENCE_THRESHOLD_RMS
|
|
1233
1236
|
let dB = Float(20 * log10(Double(rms)))
|
|
1234
1237
|
|
|
1235
1238
|
let dataPoint = DataPoint(
|
|
@@ -12,6 +12,9 @@ import UIKit
|
|
|
12
12
|
import MediaPlayer
|
|
13
13
|
import UserNotifications
|
|
14
14
|
|
|
15
|
+
// Constants
|
|
16
|
+
internal let WAV_HEADER_SIZE: Int64 = 44 // Standard WAV header is 44 bytes
|
|
17
|
+
|
|
15
18
|
// Helper to convert to little-endian byte array
|
|
16
19
|
extension UInt32 {
|
|
17
20
|
var littleEndianBytes: [UInt8] {
|
|
@@ -458,9 +461,8 @@ class AudioStreamManager: NSObject {
|
|
|
458
461
|
let baseFilename: String
|
|
459
462
|
if let existingFilename = recordingSettings?.filename {
|
|
460
463
|
baseFilename = existingFilename
|
|
461
|
-
} else if let existingUUID = recordingUUID {
|
|
462
|
-
baseFilename = existingUUID.uuidString
|
|
463
464
|
} else {
|
|
465
|
+
// Always create a new UUID for recording unless a filename is provided
|
|
464
466
|
let newUUID = UUID()
|
|
465
467
|
recordingUUID = newUUID
|
|
466
468
|
baseFilename = newUUID.uuidString
|
|
@@ -1096,17 +1098,17 @@ class AudioStreamManager: NSObject {
|
|
|
1096
1098
|
- Exists: true
|
|
1097
1099
|
- Size: \(wavFileSize) bytes
|
|
1098
1100
|
- Duration: \(finalDuration) seconds
|
|
1099
|
-
- Expected minimum size:
|
|
1101
|
+
- Expected minimum size: \(WAV_HEADER_SIZE) bytes (WAV header)
|
|
1100
1102
|
""")
|
|
1101
1103
|
|
|
1102
1104
|
// Return nil if the file is too small
|
|
1103
|
-
if wavFileSize <=
|
|
1104
|
-
Logger.debug("Recording file is too small (≤
|
|
1105
|
+
if wavFileSize <= WAV_HEADER_SIZE {
|
|
1106
|
+
Logger.debug("Recording file is too small (≤ \(WAV_HEADER_SIZE) bytes), likely no audio data was recorded")
|
|
1105
1107
|
return nil
|
|
1106
1108
|
}
|
|
1107
1109
|
|
|
1108
1110
|
// Update the WAV header with the correct file size
|
|
1109
|
-
updateWavHeader(fileURL: fileURL, totalDataSize: wavFileSize -
|
|
1111
|
+
updateWavHeader(fileURL: fileURL, totalDataSize: wavFileSize - WAV_HEADER_SIZE)
|
|
1110
1112
|
|
|
1111
1113
|
// Validate compressed file if enabled
|
|
1112
1114
|
var compression: CompressedRecordingInfo?
|
|
@@ -1372,7 +1374,7 @@ class AudioStreamManager: NSObject {
|
|
|
1372
1374
|
/// - fileURL: The URL of the WAV file.
|
|
1373
1375
|
/// - totalDataSize: The total size of the audio data.
|
|
1374
1376
|
private func updateWavHeader(fileURL: URL, totalDataSize: Int64) {
|
|
1375
|
-
// Prevent negative values - minimum WAV file size should be at least the header size (
|
|
1377
|
+
// Prevent negative values - minimum WAV file size should be at least the header size (WAV_HEADER_SIZE bytes)
|
|
1376
1378
|
guard totalDataSize >= 0 else {
|
|
1377
1379
|
Logger.debug("Invalid file size: total data size is negative")
|
|
1378
1380
|
return
|
|
@@ -1383,7 +1385,7 @@ class AudioStreamManager: NSObject {
|
|
|
1383
1385
|
defer { fileHandle.closeFile() }
|
|
1384
1386
|
|
|
1385
1387
|
// Calculate sizes
|
|
1386
|
-
let fileSize = totalDataSize +
|
|
1388
|
+
let fileSize = totalDataSize + WAV_HEADER_SIZE - 8 // Total file size minus 8 bytes for 'RIFF' and size field itself
|
|
1387
1389
|
let dataSize = totalDataSize // Size of the 'data' sub-chunk
|
|
1388
1390
|
|
|
1389
1391
|
// Update RIFF chunk size at offset 4
|
|
@@ -1585,8 +1587,19 @@ class AudioStreamManager: NSObject {
|
|
|
1585
1587
|
guard let self = self else { return }
|
|
1586
1588
|
if let processor = self.audioProcessor {
|
|
1587
1589
|
Logger.debug("Processing audio buffer of size: \(dataToProcess.count)")
|
|
1590
|
+
|
|
1591
|
+
// Strip WAV header from the first buffer to avoid false amplitude detection
|
|
1592
|
+
let dataToAnalyze: Data
|
|
1593
|
+
if self.totalDataSizeAnalysis == 0 && dataToProcess.count > Int(WAV_HEADER_SIZE) {
|
|
1594
|
+
// This is the first buffer and may contain the WAV header
|
|
1595
|
+
dataToAnalyze = dataToProcess.subdata(in: Int(WAV_HEADER_SIZE)..<dataToProcess.count)
|
|
1596
|
+
Logger.debug("Removed WAV header (\(WAV_HEADER_SIZE) bytes) from first buffer for analysis")
|
|
1597
|
+
} else {
|
|
1598
|
+
dataToAnalyze = dataToProcess
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1588
1601
|
let processingResult = processor.processAudioBuffer(
|
|
1589
|
-
data:
|
|
1602
|
+
data: dataToAnalyze,
|
|
1590
1603
|
sampleRate: Float(settings.sampleRate),
|
|
1591
1604
|
segmentDurationMs: settings.segmentDurationMs,
|
|
1592
1605
|
featureOptions: settings.featureOptions ?? [:],
|
|
@@ -1600,9 +1613,10 @@ class AudioStreamManager: NSObject {
|
|
|
1600
1613
|
}
|
|
1601
1614
|
}
|
|
1602
1615
|
|
|
1603
|
-
|
|
1616
|
+
// Update state after emission
|
|
1604
1617
|
self.lastEmissionTimeAnalysis = currentTime
|
|
1605
|
-
|
|
1618
|
+
// Update the total analysis data size to mark that we've processed data
|
|
1619
|
+
self.totalDataSizeAnalysis = self.totalDataSize
|
|
1606
1620
|
accumulatedAnalysisData.removeAll()
|
|
1607
1621
|
}
|
|
1608
1622
|
}
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import ExpoModulesCore
|
|
3
3
|
import AVFoundation
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
let
|
|
7
|
-
let
|
|
5
|
+
// Constants
|
|
6
|
+
private let audioDataEvent: String = "AudioData"
|
|
7
|
+
private let audioAnalysisEvent: String = "AudioAnalysis"
|
|
8
|
+
private let recordingInterruptedEvent: String = "onRecordingInterrupted"
|
|
9
|
+
private let DEFAULT_SEGMENT_DURATION_MS = 100
|
|
8
10
|
|
|
9
11
|
public class ExpoAudioStreamModule: Module, AudioStreamManagerDelegate {
|
|
10
12
|
private var streamManager = AudioStreamManager()
|
|
@@ -60,7 +62,7 @@ public class ExpoAudioStreamModule: Module, AudioStreamManagerDelegate {
|
|
|
60
62
|
|
|
61
63
|
let features = options["features"] as? [String: Bool] ?? [:]
|
|
62
64
|
let featureOptions = self.extractFeatureOptions(from: features)
|
|
63
|
-
let segmentDurationMs = options["segmentDurationMs"] as? Int ??
|
|
65
|
+
let segmentDurationMs = options["segmentDurationMs"] as? Int ?? DEFAULT_SEGMENT_DURATION_MS // Default value of 100ms
|
|
64
66
|
|
|
65
67
|
DispatchQueue.global().async(execute: {
|
|
66
68
|
do {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@siteed/expo-audio-studio",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "build/index.js",
|
package/plugin/build/index.js
CHANGED
|
@@ -113,10 +113,9 @@ const withRecordingPermission = (config, props) => {
|
|
|
113
113
|
];
|
|
114
114
|
const optionalPermissions = [
|
|
115
115
|
enableNotifications && 'android.permission.POST_NOTIFICATIONS',
|
|
116
|
-
enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE',
|
|
116
|
+
enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE', // Only add if enabled
|
|
117
117
|
enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE',
|
|
118
|
-
enableBackgroundAudio &&
|
|
119
|
-
'android.permission.FOREGROUND_SERVICE_MICROPHONE',
|
|
118
|
+
enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
|
|
120
119
|
].filter(Boolean);
|
|
121
120
|
const permissionsToAdd = [...basePermissions, ...optionalPermissions];
|
|
122
121
|
debugLog('📋 Existing Android permissions:', config.modResults.manifest['uses-permission']?.map((p) => p.$?.['android:name']) || []);
|
package/plugin/src/index.ts
CHANGED
|
@@ -169,10 +169,9 @@ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
|
|
|
169
169
|
|
|
170
170
|
const optionalPermissions = [
|
|
171
171
|
enableNotifications && 'android.permission.POST_NOTIFICATIONS',
|
|
172
|
-
enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE',
|
|
172
|
+
enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE', // Only add if enabled
|
|
173
173
|
enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE',
|
|
174
|
-
enableBackgroundAudio &&
|
|
175
|
-
'android.permission.FOREGROUND_SERVICE_MICROPHONE',
|
|
174
|
+
enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
|
|
176
175
|
].filter(Boolean) as string[]
|
|
177
176
|
|
|
178
177
|
const permissionsToAdd = [...basePermissions, ...optionalPermissions]
|