@siteed/expo-audio-stream 1.11.1 → 1.11.3

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 CHANGED
@@ -8,25 +8,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  ## [Unreleased]
9
9
 
10
10
 
11
+ ## [1.11.3] - 2025-01-25
12
+ - disable duplicate notification alerts for audio stream (#82) ([12f9992](https://github.com/deeeed/expo-audio-stream/commit/12f999247cdd6b08753bcf1b481582a604826383))
13
+ - feat(deps): update expo packages and dependencies to latest patch versions (#81) ([3ed0526](https://github.com/deeeed/expo-audio-stream/commit/3ed0526545623530a10757f1bbd7f877a2c31296))
14
+
15
+ ## [1.11.2] - 2025-01-22
16
+ - resources not cleanup properly on app kill (#80) ([7d522a5](https://github.com/deeeed/expo-audio-stream/commit/7d522a531e70065b99758aa3a4c669769fdbd110))
17
+
18
+
11
19
  ## [1.11.1] - 2025-01-22
12
20
  - chore: force deployment of 1.11.1
13
21
 
22
+
23
+
14
24
  ## [1.11.0] - 2025-01-22
15
25
  - feat(audio): add intelligent call interruption handling & compression improvements ([f8f6187](https://github.com/deeeed/expo-audio-stream/pull/78))
16
26
 
17
27
 
28
+
29
+
18
30
  ## [1.10.0] - 2025-01-14
19
31
  - add support for pausing and resuming compressed recordings ([bc3f629](https://github.com/deeeed/expo-audio-stream/commit/bc3f6295d060396325e0f008ff00b3be9c8722cd))
20
32
  - optimize notification channel settings ([daa075e](https://github.com/deeeed/expo-audio-stream/commit/daa075e668f8faf0b8d2849e18c37384bdd293b8))
21
33
 
22
34
 
23
35
 
36
+
37
+
24
38
  ## [1.9.2] - 2025-01-12
25
39
  - ios bitrate verification to prevent invalid values ([035a180](https://github.com/deeeed/expo-audio-stream/commit/035a1800833264edcc59724aaa8a2e12d5c78dc2))
26
40
 
27
41
 
28
42
 
29
43
 
44
+
45
+
30
46
  ## [1.9.1] - 2025-01-12
31
47
  - ios potentially missing compressed file info ([88a628c](https://github.com/deeeed/expo-audio-stream/commit/88a628c35f2bfd626a2a5de1eb6950efd814619d))
32
48
 
@@ -34,6 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
34
50
 
35
51
 
36
52
 
53
+
54
+
37
55
  ## [1.9.0] - 2025-01-11
38
56
  - feat(web-audio): optimize memory usage and streaming performance for web audio recording (#75) ([7b93e12](https://github.com/deeeed/expo-audio-stream/commit/7b93e12aae4bc0599b06b48ca34a60f65587fc75))
39
57
 
@@ -42,6 +60,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
42
60
 
43
61
 
44
62
 
63
+
64
+
45
65
  ## [1.8.0] - 2025-01-10
46
66
  - feat(audio): implement audio compression support ([ff4e060](https://github.com/deeeed/expo-audio-stream/commit/ff4e060fef1061804c1cc0126d4344d2d50daa9a))
47
67
 
@@ -51,6 +71,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
51
71
 
52
72
 
53
73
 
74
+
75
+
54
76
  ## [1.7.2] - 2025-01-07
55
77
  - fix(audio-stream): correct WAV header handling in web audio recording ([9ba7de5](https://github.com/deeeed/expo-audio-stream/commit/9ba7de5b96ca4cc937dea261c80d3fda9c99e8f4))
56
78
 
@@ -61,6 +83,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
61
83
 
62
84
 
63
85
 
86
+
87
+
64
88
  ## [1.7.1] - 2025-01-07
65
89
  - update notification to avoid triggering new alerts (#71) ([32dcfc5](https://github.com/deeeed/expo-audio-stream/commit/32dcfc55daf3236babefc17016f329c177d466fd))
66
90
 
@@ -72,6 +96,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
72
96
 
73
97
 
74
98
 
99
+
100
+
75
101
  ## [1.7.0] - 2025-01-05
76
102
  - feat(playground): enhance app configuration and build setup for production deployment (#58) ([929d443](https://github.com/deeeed/expo-audio-stream/commit/929d443145378b1430d215db5c00b13758420e2b))
77
103
  - chore(expo-audio-stream): release @siteed/expo-audio-stream@1.6.1 ([084e8ad](https://github.com/deeeed/expo-audio-stream/commit/084e8adb91da7874c9e608b55d9c7b2ffd7a8327))
@@ -89,6 +115,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
89
115
 
90
116
 
91
117
 
118
+
119
+
92
120
  ## [1.6.1] - 2024-12-11
93
121
  - chore(expo-audio-stream): remove git commit step from publish script ([4a772ce](https://github.com/deeeed/expo-audio-stream/commit/4a772ce93bb7405d9b8e981f46bdf8941a71ecfe))
94
122
  - chore: more publishing automation ([3693021](https://github.com/deeeed/expo-audio-stream/commit/369302107f9dca9dddd8ae68e6214481a39976ac))
@@ -110,6 +138,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
110
138
 
111
139
 
112
140
 
141
+
142
+
113
143
  ## [1.5.0] - 2024-12-10
114
144
  - UNPUBLISHED because of a bug in the build system
115
145
 
@@ -124,6 +154,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
124
154
 
125
155
 
126
156
 
157
+
158
+
127
159
  ## [1.4.0] - 2024-12-05
128
160
  - chore: remove unusded dependencies ([ad81dd5](https://github.com/deeeed/expo-audio-stream/commit/ad81dd560c93dd1d04995a323a4ae72d4de20f3e))
129
161
 
@@ -138,6 +170,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
138
170
 
139
171
 
140
172
 
173
+
174
+
141
175
  ## [1.3.1] - 2024-12-05
142
176
  - feat(web): implement throttling and optimize event processing (#49) ([da28765](https://github.com/deeeed/expo-audio-stream/commit/da2876524c2c9d6e0a980fde40a0197b929d8a7f))
143
177
 
@@ -152,6 +186,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
152
186
 
153
187
 
154
188
 
189
+
190
+
155
191
  ## [1.3.0] - 2024-11-28
156
192
  ### Added
157
193
  - refactor(permissions): standardize permission status response structure across platforms (#44) ([7c9c800](https://github.com/deeeed/expo-audio-stream/commit/7c9c800d83b7cea3516643371484d5e1f3b99e4c))
@@ -171,6 +207,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
171
207
 
172
208
 
173
209
 
210
+
211
+
174
212
  ## [1.2.5] - 2024-11-12
175
213
  ### Added
176
214
  - docs(license): add MIT license to all packages (6 files changed)
@@ -187,6 +225,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
187
225
 
188
226
 
189
227
 
228
+
229
+
190
230
  ## [1.2.4] - 2024-11-05
191
231
  ### Changed
192
232
  - Android minimum audio interval set to 10ms.
@@ -206,6 +246,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
206
246
 
207
247
 
208
248
 
249
+
250
+
209
251
  ## [1.2.0] - 2024-10-24
210
252
  ### Added
211
253
  - Feature: Keep device awake during recording with `keepAwake` option
@@ -225,6 +267,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
225
267
 
226
268
 
227
269
 
270
+
271
+
228
272
  ## [1.1.17] - 2024-10-21
229
273
  ### Added
230
274
  - Support bluetooth headset on ios
@@ -241,6 +285,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
241
285
 
242
286
 
243
287
 
288
+
289
+
244
290
  ## [1.0.0] - 2024-04-01
245
291
  ### Added
246
292
  - Initial release of @siteed/expo-audio-stream.
@@ -251,7 +297,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
251
297
  - Feature: Audio features extraction during recording.
252
298
  - Feature: Consistent WAV PCM recording format across all platforms.
253
299
 
254
- [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.11.1...HEAD
300
+ [unreleased]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.11.3...HEAD
301
+ [1.11.3]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.11.2...@siteed/expo-audio-stream@1.11.3
302
+ [1.11.2]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.11.1...@siteed/expo-audio-stream@1.11.2
255
303
  [1.11.1]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.11.0...@siteed/expo-audio-stream@1.11.1
256
304
  [1.11.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.10.0...@siteed/expo-audio-stream@1.11.0
257
305
  [1.10.0]: https://github.com/deeeed/expo-audio-stream/compare/@siteed/expo-audio-stream@1.9.2...@siteed/expo-audio-stream@1.10.0
@@ -115,10 +115,12 @@ class AudioNotificationManager private constructor(context: Context) {
115
115
  PendingIntent.FLAG_IMMUTABLE
116
116
  )
117
117
 
118
+ // Configure notification builder with settings optimized for recording service
119
+ // and wearable device compatibility
118
120
  notificationBuilder = NotificationCompat.Builder(context, recordingConfig.notification.channelId)
119
121
  .setSmallIcon(iconResId)
120
122
  .setContentIntent(pendingIntent)
121
- .setOngoing(true)
123
+ .setOngoing(true) // Notification cannot be dismissed by user
122
124
  .setPriority(NotificationCompat.PRIORITY_HIGH)
123
125
  .setCategory(NotificationCompat.CATEGORY_SERVICE)
124
126
  .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
@@ -126,6 +128,11 @@ class AudioNotificationManager private constructor(context: Context) {
126
128
  .setCustomContentView(remoteViews)
127
129
  .setCustomBigContentView(remoteViews)
128
130
  .setStyle(NotificationCompat.DecoratedCustomViewStyle())
131
+ // Prevent repeated alerts and vibrations
132
+ .setOnlyAlertOnce(true) // Only alert on first notification
133
+ .setVibrate(null) // Disable vibration
134
+ .setDefaults(0) // Clear all default notification behaviors
135
+ .setLocalOnly(true) // Prevent notification from appearing on wearable devices
129
136
 
130
137
  addNotificationActions(context)
131
138
  }
@@ -28,6 +28,7 @@ import android.media.AudioAttributes
28
28
  import android.media.AudioFocusRequest
29
29
  import android.telephony.PhoneStateListener
30
30
  import android.telephony.TelephonyManager
31
+ import android.app.ActivityManager
31
32
 
32
33
  class AudioRecorderManager(
33
34
  private val context: Context,
@@ -137,6 +138,11 @@ class AudioRecorderManager(
137
138
  ).also { instance = it }
138
139
  }
139
140
  }
141
+
142
+ fun destroy() {
143
+ instance?.cleanup()
144
+ instance = null
145
+ }
140
146
  }
141
147
 
142
148
  @RequiresApi(Build.VERSION_CODES.R)
@@ -641,15 +647,28 @@ class AudioRecorderManager(
641
647
 
642
648
  fun getStatus(): Bundle {
643
649
  synchronized(audioRecordLock) {
650
+ // Check if service is actually running
651
+ val isServiceRunning = context.let { ctx ->
652
+ val manager = ctx.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
653
+ manager?.getRunningServices(Integer.MAX_VALUE)
654
+ ?.any { it.service.className == AudioRecordingService::class.java.name }
655
+ } ?: false
656
+
657
+ // If service is running but we think we're not recording, clean up
658
+ if (isServiceRunning && !isRecording.get()) {
659
+ Log.d(Constants.TAG, "Detected orphaned recording service, cleaning up...")
660
+ cleanup()
661
+ AudioRecordingService.stopService(context)
662
+ }
663
+
644
664
  if (!isRecording.get()) {
645
665
  Log.d(Constants.TAG, "Not recording --- skip status with default values")
646
-
647
666
  return bundleOf(
648
667
  "isRecording" to false,
649
668
  "isPaused" to false,
650
669
  "mime" to mimeType,
651
670
  "size" to 0,
652
- "interval" to recordingConfig.interval,
671
+ "interval" to (recordingConfig?.interval ?: 0)
653
672
  )
654
673
  }
655
674
 
@@ -916,27 +935,51 @@ class AudioRecorderManager(
916
935
  isFirstChunk = false
917
936
  }
918
937
 
919
- private fun cleanup() {
920
- try {
921
- if (recordingConfig.showNotification) {
922
- notificationManager.stopUpdates()
923
- AudioRecordingService.stopService(context)
924
- }
938
+ fun cleanup() {
939
+ synchronized(audioRecordLock) {
940
+ try {
941
+ if (isRecording.get()) {
942
+ audioRecord?.stop()
943
+ compressedRecorder?.stop()
944
+ compressedRecorder?.release()
945
+ }
946
+
947
+ isRecording.set(false)
948
+ isPaused.set(false)
949
+
950
+ if (recordingConfig.showNotification) {
951
+ notificationManager.stopUpdates()
952
+ AudioRecordingService.stopService(context)
953
+ }
925
954
 
926
- // Reset all states
927
- isRecording.set(false)
928
- isPaused.set(false)
929
- totalRecordedTime = 0
930
- pausedDuration = 0
931
- lastEmittedSize = 0
932
- recordingStartTime = 0
933
- } catch (e: Exception) {
934
- Log.e(Constants.TAG, "Error in cleanup", e)
935
- } finally {
936
- releaseWakeLock()
955
+ releaseWakeLock()
956
+ releaseAudioFocus()
957
+ audioRecord?.release()
958
+ audioRecord = null
959
+
960
+ // Reset all state
961
+ totalRecordedTime = 0
962
+ pausedDuration = 0
963
+ lastEmittedSize = 0
964
+ recordingStartTime = 0
965
+
966
+ // Update the WAV header if needed
967
+ audioFile?.let { file ->
968
+ audioFileHandler.updateWavHeader(file)
969
+ }
970
+
971
+ // Send event to notify that recording was stopped
972
+ eventSender.sendExpoEvent(Constants.RECORDING_INTERRUPTED_EVENT_NAME, bundleOf(
973
+ "reason" to "appKilled",
974
+ "isPaused" to false
975
+ ))
976
+ } catch (e: Exception) {
977
+ Log.e(Constants.TAG, "Error during cleanup", e)
978
+ }
937
979
  }
938
980
  }
939
981
 
982
+ @RequiresApi(Build.VERSION_CODES.Q)
940
983
  private fun initializeCompressedRecorder(fileExtension: String, promise: Promise): Boolean {
941
984
  try {
942
985
  // Use the existing audioFileHandler instance
@@ -8,6 +8,7 @@ import android.os.IBinder
8
8
  import android.util.Log
9
9
  import android.os.Handler
10
10
  import android.os.Looper
11
+ import expo.modules.kotlin.Promise
11
12
 
12
13
  class AudioRecordingService : Service() {
13
14
  private val notificationManager by lazy {
@@ -43,17 +44,46 @@ class AudioRecordingService : Service() {
43
44
  override fun onDestroy() {
44
45
  Log.d(Constants.TAG, "AudioRecordingService onDestroy")
45
46
 
46
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
47
- stopForeground(STOP_FOREGROUND_REMOVE)
48
- } else {
49
- @Suppress("DEPRECATION")
50
- stopForeground(true)
51
- }
47
+ stopForeground(STOP_FOREGROUND_REMOVE)
52
48
 
53
49
  isRunning = false
54
50
  super.onDestroy()
55
51
  }
56
52
 
53
+ override fun onTaskRemoved(rootIntent: Intent?) {
54
+ super.onTaskRemoved(rootIntent)
55
+ Log.d(Constants.TAG, "AudioRecordingService onTaskRemoved")
56
+
57
+ // Stop recording when app is killed
58
+ AudioRecorderManager.getInstance()?.let { manager ->
59
+ mainHandler.post {
60
+ // Create a simple promise object for internal use
61
+ val promise = object : Promise {
62
+ override fun resolve(value: Any?) {
63
+ Log.d(Constants.TAG, "Successfully stopped recording on task removed")
64
+ cleanup()
65
+ }
66
+ override fun reject(code: String, message: String?, cause: Throwable?) {
67
+ Log.e(Constants.TAG, "Failed to stop recording on task removed: $message")
68
+ cleanup()
69
+ }
70
+ }
71
+
72
+ try {
73
+ manager.stopRecording(promise)
74
+ } catch (e: Exception) {
75
+ promise.reject("ERROR", e.message, e)
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+
82
+ private fun cleanup() {
83
+ stopForeground(STOP_FOREGROUND_REMOVE)
84
+ stopSelf()
85
+ }
86
+
57
87
  companion object {
58
88
  fun startService(context: Context) {
59
89
  val serviceIntent = Intent(context, AudioRecordingService::class.java)
@@ -1,6 +1,8 @@
1
1
  package net.siteed.audiostream
2
2
 
3
3
  import android.Manifest
4
+ import android.app.ActivityManager
5
+ import android.content.Context
4
6
  import android.os.Build
5
7
  import android.os.Bundle
6
8
  import android.util.Log
@@ -171,6 +173,29 @@ class ExpoAudioStreamModule : Module(), EventSender {
171
173
  )
172
174
  }
173
175
  }
176
+
177
+ OnDestroy {
178
+ AudioRecorderManager.destroy()
179
+ }
180
+
181
+ // Add a new function to check if recording is actually running
182
+ AsyncFunction("checkRecordingStatus") { promise: Promise ->
183
+ val isServiceRunning = AudioRecordingService::class.java.name.let { className ->
184
+ val manager = appContext.reactContext?.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
185
+ manager?.getRunningServices(Integer.MAX_VALUE)
186
+ ?.any { it.service.className == className }
187
+ } ?: false
188
+
189
+ val status = audioRecorderManager.getStatus()
190
+
191
+ // If service is running but isRecording is false, we need to cleanup
192
+ if (isServiceRunning && !status.getBoolean("isRecording")) {
193
+ audioRecorderManager.cleanup()
194
+ AudioRecordingService.stopService(appContext.reactContext!!)
195
+ }
196
+
197
+ promise.resolve(status)
198
+ }
174
199
  }
175
200
 
176
201
  private fun initializeManager() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteed/expo-audio-stream",
3
- "version": "1.11.1",
3
+ "version": "1.11.3",
4
4
  "description": "stream audio crossplatform",
5
5
  "license": "MIT",
6
6
  "main": "build/index.js",
@@ -66,12 +66,12 @@
66
66
  "release": "./publish.sh"
67
67
  },
68
68
  "devDependencies": {
69
- "@expo/config-plugins": "~8.0.0",
69
+ "@expo/config-plugins": "~9.0.0",
70
70
  "@siteed/publisher": "^0.4.15",
71
71
  "@size-limit/preset-big-lib": "^11.1.4",
72
72
  "@types/jest": "^29.5.12",
73
73
  "@types/node": "^20.12.7",
74
- "@types/react": "~18.2.79",
74
+ "@types/react": "~18.3.12",
75
75
  "@typescript-eslint/eslint-plugin": "^7.7.0",
76
76
  "@typescript-eslint/parser": "^7.7.0",
77
77
  "bundle-size": "^1.1.5",
@@ -82,11 +82,11 @@
82
82
  "eslint-plugin-prettier": "^5.1.3",
83
83
  "eslint-plugin-promise": "^6.1.1",
84
84
  "eslint-plugin-react": "^7.34.1",
85
- "expo": "~52.0.0",
85
+ "expo": "^52.0.27",
86
86
  "expo-module-scripts": "^4.0.2",
87
87
  "jest": "^29.7.0",
88
88
  "prettier": "^3.2.5",
89
- "react-native": "0.76.0",
89
+ "react-native": "0.76.6",
90
90
  "rimraf": "^6.0.1",
91
91
  "size-limit": "^11.1.4",
92
92
  "ts-node": "^10.9.2",
@@ -105,6 +105,6 @@
105
105
  },
106
106
  "dependencies": {
107
107
  "@siteed/design-system": "^0.35.1",
108
- "expo-modules-core": "^2.1.1"
108
+ "expo-modules-core": "~2.1.4"
109
109
  }
110
110
  }