@siteed/expo-audio-stream 1.9.2 → 1.11.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.
@@ -16,89 +16,112 @@ function debugLog(message: string, ...args: unknown[]): void {
16
16
  }
17
17
  }
18
18
 
19
- const withRecordingPermission: ConfigPlugin = (config: ExpoConfig) => {
20
- debugLog('📱 Configuring Recording Permissions Plugin...')
19
+ interface AudioStreamPluginOptions {
20
+ enablePhoneStateHandling?: boolean
21
+ enableNotifications?: boolean
22
+ enableBackgroundAudio?: boolean
23
+ }
24
+
25
+ const withRecordingPermission: ConfigPlugin<AudioStreamPluginOptions> = (
26
+ config: ExpoConfig,
27
+ props: AudioStreamPluginOptions | void
28
+ ) => {
29
+ // Default options if pluginOptions is undefined (void)
30
+ const options: AudioStreamPluginOptions = {
31
+ enablePhoneStateHandling: true,
32
+ enableNotifications: true,
33
+ enableBackgroundAudio: true,
34
+ ...(props || {}),
35
+ }
36
+
37
+ const {
38
+ enablePhoneStateHandling,
39
+ enableNotifications,
40
+ enableBackgroundAudio,
41
+ } = options
42
+
43
+ debugLog('📱 Configuring Recording Permissions Plugin...', options)
21
44
 
22
45
  // iOS Configuration
23
46
  config = withInfoPlist(config as any, (config) => {
24
- debugLog('🍎 Configuring iOS permissions and capabilities...')
25
-
26
- // Existing microphone permission
47
+ // Base microphone permission (always required)
27
48
  config.modResults['NSMicrophoneUsageDescription'] =
28
49
  config.modResults['NSMicrophoneUsageDescription'] ||
29
50
  MICROPHONE_USAGE
30
51
 
31
- // Add notification permissions
32
- config.modResults['NSUserNotificationsUsageDescription'] =
33
- NOTIFICATION_USAGE
34
-
35
- // Add notification style
36
- config.modResults['NSUserNotificationAlertStyle'] = 'alert'
52
+ if (enableNotifications) {
53
+ config.modResults['NSUserNotificationsUsageDescription'] =
54
+ NOTIFICATION_USAGE
55
+ config.modResults['NSUserNotificationAlertStyle'] = 'alert'
56
+ }
37
57
 
38
- // Background modes
39
58
  const existingBackgroundModes =
40
59
  config.modResults.UIBackgroundModes || []
41
- if (!existingBackgroundModes.includes('audio')) {
60
+
61
+ if (
62
+ enableBackgroundAudio &&
63
+ !existingBackgroundModes.includes('audio')
64
+ ) {
42
65
  existingBackgroundModes.push('audio')
43
66
  }
44
- if (!existingBackgroundModes.includes('remote-notification')) {
45
- existingBackgroundModes.push('remote-notification')
46
- }
47
- config.modResults.UIBackgroundModes = existingBackgroundModes
48
67
 
49
- debugLog('iOS Background Modes:', config.modResults.UIBackgroundModes)
68
+ if (enablePhoneStateHandling) {
69
+ if (!existingBackgroundModes.includes('voip')) {
70
+ existingBackgroundModes.push('voip')
71
+ }
72
+ const existingCapabilities = (config.modResults
73
+ .UIRequiredDeviceCapabilities || []) as string[]
74
+ if (!existingCapabilities.includes('telephony')) {
75
+ existingCapabilities.push('telephony')
76
+ }
77
+ config.modResults.UIRequiredDeviceCapabilities =
78
+ existingCapabilities
79
+ }
50
80
 
81
+ config.modResults.UIBackgroundModes = existingBackgroundModes
51
82
  return config
52
83
  })
53
84
 
54
85
  // Android Configuration
55
86
  config = withAndroidManifest(config as any, (config) => {
56
- debugLog('🤖 Configuring Android Manifest...')
57
-
58
- const androidManifest = config.modResults
59
- if (!androidManifest.manifest) {
60
- console.error(`${LOG_PREFIX} ❌ Android Manifest is null - plugin cannot continue`)
61
- return config
62
- }
63
-
64
- // Add xmlns:android attribute to manifest
65
- androidManifest.manifest.$ = {
66
- ...androidManifest.manifest.$,
67
- 'xmlns:android': 'http://schemas.android.com/apk/res/android',
68
- }
69
-
70
- // Ensure permissions array exists
71
- if (!androidManifest.manifest['uses-permission']) {
72
- androidManifest.manifest['uses-permission'] = []
73
- }
74
-
75
- const { addPermission } = AndroidConfig.Permissions
76
-
77
- debugLog('📋 Existing Android permissions:',
78
- androidManifest.manifest['uses-permission']?.map(p => p.$?.['android:name']) || [])
79
-
80
- const permissionsToAdd = [
87
+ const basePermissions = [
81
88
  'android.permission.RECORD_AUDIO',
82
- 'android.permission.FOREGROUND_SERVICE',
83
- 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
84
89
  'android.permission.WAKE_LOCK',
85
- 'android.permission.POST_NOTIFICATIONS',
86
90
  ]
87
91
 
92
+ const optionalPermissions = [
93
+ enableNotifications && 'android.permission.POST_NOTIFICATIONS',
94
+ enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE',
95
+ enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE',
96
+ enableBackgroundAudio &&
97
+ 'android.permission.FOREGROUND_SERVICE_MICROPHONE',
98
+ ].filter(Boolean) as string[]
99
+
100
+ const permissionsToAdd = [...basePermissions, ...optionalPermissions]
101
+
102
+ debugLog(
103
+ '📋 Existing Android permissions:',
104
+ config.modResults.manifest['uses-permission']?.map(
105
+ (p) => p.$?.['android:name']
106
+ ) || []
107
+ )
108
+
88
109
  debugLog('➕ Adding Android permissions:', permissionsToAdd)
89
110
 
111
+ const { addPermission } = AndroidConfig.Permissions
112
+
90
113
  // Add each permission only if it doesn't exist
91
114
  permissionsToAdd.forEach((permission) => {
92
- const existingPermission = androidManifest.manifest[
115
+ const existingPermission = config.modResults.manifest[
93
116
  'uses-permission'
94
117
  ]?.find((p) => p.$?.['android:name'] === permission)
95
118
  if (!existingPermission) {
96
- addPermission(androidManifest, permission)
119
+ addPermission(config.modResults, permission)
97
120
  }
98
121
  })
99
122
 
100
123
  // Get the main application node
101
- const mainApplication = androidManifest.manifest.application?.[0]
124
+ const mainApplication = config.modResults.manifest.application?.[0]
102
125
  if (mainApplication) {
103
126
  debugLog('📱 Configuring Android application components...')
104
127
 
@@ -163,7 +186,9 @@ const withRecordingPermission: ConfigPlugin = (config: ExpoConfig) => {
163
186
 
164
187
  debugLog('✅ AudioRecordingService configured')
165
188
  } else {
166
- console.error(`${LOG_PREFIX} ❌ Main application node not found in Android Manifest`)
189
+ console.error(
190
+ `${LOG_PREFIX} ❌ Main application node not found in Android Manifest`
191
+ )
167
192
  }
168
193
 
169
194
  return config
@@ -118,6 +118,19 @@ export interface IOSConfig {
118
118
  audioSession?: AudioSessionConfig
119
119
  }
120
120
 
121
+ // Add new type for interruption reasons
122
+ export type RecordingInterruptionReason =
123
+ | 'audioFocusLoss'
124
+ | 'audioFocusGain'
125
+ | 'phoneCall'
126
+ | 'phoneCallEnded'
127
+
128
+ // Add new interface for interruption events
129
+ export interface RecordingInterruptionEvent {
130
+ reason: RecordingInterruptionReason
131
+ isPaused: boolean
132
+ }
133
+
121
134
  export interface RecordingConfig {
122
135
  // Sample rate for recording (16000, 44100, or 48000 Hz)
123
136
  sampleRate?: SampleRate
@@ -169,6 +182,12 @@ export interface RecordingConfig {
169
182
  format: 'aac' | 'opus' | 'mp3'
170
183
  bitrate?: number
171
184
  }
185
+
186
+ // Whether to automatically resume recording after an interruption (default is false)
187
+ autoResumeAfterInterruption?: boolean
188
+
189
+ // Optional callback to handle recording interruptions
190
+ onRecordingInterrupted?: (_: RecordingInterruptionEvent) => void
172
191
  }
173
192
 
174
193
  export interface NotificationConfig {
@@ -249,4 +268,5 @@ export interface UseAudioRecorderState {
249
268
  size: number // Size in bytes of the recorded audio
250
269
  compression?: CompressionInfo
251
270
  analysisData?: AudioAnalysis // Analysis data for the recording depending on enableProcessing flag
271
+ onRecordingInterrupted?: (_: RecordingInterruptionEvent) => void
252
272
  }