react-native-audio-api 0.11.0-alpha.3 → 0.11.0-alpha.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.
Files changed (151) hide show
  1. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +34 -6
  2. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +4 -0
  3. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/ptrs.hpp +8 -0
  4. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.cpp +4 -0
  5. package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +1 -0
  6. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +164 -16
  7. package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioPlayer.kt +10 -8
  8. package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioRecorder.kt +10 -8
  9. package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +3 -4
  10. package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +128 -0
  11. package/android/src/main/java/com/swmansion/audioapi/system/ForegroundServiceManager.kt +116 -0
  12. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +115 -107
  13. package/android/src/main/java/com/swmansion/audioapi/system/PermissionRequestListener.kt +2 -1
  14. package/android/src/main/java/com/swmansion/audioapi/system/notification/BaseNotification.kt +47 -0
  15. package/android/src/main/java/com/swmansion/audioapi/system/notification/NotificationRegistry.kt +191 -0
  16. package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +669 -0
  17. package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt +33 -0
  18. package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotification.kt +303 -0
  19. package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +45 -0
  20. package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt +119 -0
  21. package/common/cpp/audioapi/core/utils/AudioFileWriter.h +1 -0
  22. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +1 -0
  23. package/common/cpp/audioapi/utils/AudioFileProperties.h +17 -17
  24. package/ios/audioapi/ios/AudioAPIModule.h +2 -2
  25. package/ios/audioapi/ios/AudioAPIModule.mm +108 -18
  26. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +8 -7
  27. package/ios/audioapi/ios/core/NativeAudioPlayer.m +1 -1
  28. package/ios/audioapi/ios/core/NativeAudioRecorder.m +9 -2
  29. package/ios/audioapi/ios/system/AudioEngine.h +2 -0
  30. package/ios/audioapi/ios/system/AudioEngine.mm +49 -6
  31. package/ios/audioapi/ios/system/AudioSessionManager.mm +12 -9
  32. package/ios/audioapi/ios/system/NotificationManager.mm +7 -4
  33. package/ios/audioapi/ios/system/notification/BaseNotification.h +58 -0
  34. package/ios/audioapi/ios/system/notification/NotificationRegistry.h +70 -0
  35. package/ios/audioapi/ios/system/notification/NotificationRegistry.mm +172 -0
  36. package/ios/audioapi/ios/system/notification/PlaybackNotification.h +27 -0
  37. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +427 -0
  38. package/lib/commonjs/api.js +72 -1
  39. package/lib/commonjs/api.js.map +1 -1
  40. package/lib/commonjs/api.web.js +27 -14
  41. package/lib/commonjs/api.web.js.map +1 -1
  42. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
  43. package/lib/commonjs/system/AudioManager.js +6 -9
  44. package/lib/commonjs/system/AudioManager.js.map +1 -1
  45. package/lib/commonjs/system/index.js +13 -0
  46. package/lib/commonjs/system/index.js.map +1 -1
  47. package/lib/commonjs/system/notification/PlaybackNotificationManager.js +135 -0
  48. package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -0
  49. package/lib/commonjs/system/notification/RecordingNotificationManager.js +182 -0
  50. package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -0
  51. package/lib/commonjs/system/notification/SimpleNotificationManager.js +122 -0
  52. package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -0
  53. package/lib/commonjs/system/notification/index.js +45 -0
  54. package/lib/commonjs/system/notification/index.js.map +1 -0
  55. package/lib/commonjs/system/notification/types.js +6 -0
  56. package/lib/commonjs/system/notification/types.js.map +1 -0
  57. package/lib/commonjs/types.js +17 -17
  58. package/lib/commonjs/types.js.map +1 -1
  59. package/lib/commonjs/web-system/index.js +17 -0
  60. package/lib/commonjs/web-system/index.js.map +1 -0
  61. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +34 -0
  62. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -0
  63. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +34 -0
  64. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -0
  65. package/lib/commonjs/web-system/notification/index.js +21 -0
  66. package/lib/commonjs/web-system/notification/index.js.map +1 -0
  67. package/lib/module/api.js +4 -0
  68. package/lib/module/api.js.map +1 -1
  69. package/lib/module/api.web.js +3 -1
  70. package/lib/module/api.web.js.map +1 -1
  71. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
  72. package/lib/module/system/AudioManager.js +6 -9
  73. package/lib/module/system/AudioManager.js.map +1 -1
  74. package/lib/module/system/index.js +1 -0
  75. package/lib/module/system/index.js.map +1 -1
  76. package/lib/module/system/notification/PlaybackNotificationManager.js +131 -0
  77. package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -0
  78. package/lib/module/system/notification/RecordingNotificationManager.js +178 -0
  79. package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -0
  80. package/lib/module/system/notification/SimpleNotificationManager.js +118 -0
  81. package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -0
  82. package/lib/module/system/notification/index.js +7 -0
  83. package/lib/module/system/notification/index.js.map +1 -0
  84. package/lib/module/system/notification/types.js +4 -0
  85. package/lib/module/system/notification/types.js.map +1 -0
  86. package/lib/module/types.js +17 -17
  87. package/lib/module/types.js.map +1 -1
  88. package/lib/module/web-system/index.js +4 -0
  89. package/lib/module/web-system/index.js.map +1 -0
  90. package/lib/module/web-system/notification/PlaybackNotificationManager.js +30 -0
  91. package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -0
  92. package/lib/module/web-system/notification/RecordingNotificationManager.js +30 -0
  93. package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -0
  94. package/lib/module/web-system/notification/index.js +5 -0
  95. package/lib/module/web-system/notification/index.js.map +1 -0
  96. package/lib/typescript/api.d.ts +2 -0
  97. package/lib/typescript/api.d.ts.map +1 -1
  98. package/lib/typescript/api.web.d.ts +3 -1
  99. package/lib/typescript/api.web.d.ts.map +1 -1
  100. package/lib/typescript/events/types.d.ts +3 -3
  101. package/lib/typescript/events/types.d.ts.map +1 -1
  102. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +16 -5
  103. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
  104. package/lib/typescript/system/AudioManager.d.ts +4 -5
  105. package/lib/typescript/system/AudioManager.d.ts.map +1 -1
  106. package/lib/typescript/system/index.d.ts +1 -0
  107. package/lib/typescript/system/index.d.ts.map +1 -1
  108. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +22 -0
  109. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -0
  110. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +23 -0
  111. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -0
  112. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +20 -0
  113. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -0
  114. package/lib/typescript/system/notification/index.d.ts +5 -0
  115. package/lib/typescript/system/notification/index.d.ts.map +1 -0
  116. package/lib/typescript/system/notification/types.d.ts +65 -0
  117. package/lib/typescript/system/notification/types.d.ts.map +1 -0
  118. package/lib/typescript/system/types.d.ts +0 -16
  119. package/lib/typescript/system/types.d.ts.map +1 -1
  120. package/lib/typescript/types.d.ts +16 -16
  121. package/lib/typescript/types.d.ts.map +1 -1
  122. package/lib/typescript/web-system/index.d.ts +2 -0
  123. package/lib/typescript/web-system/index.d.ts.map +1 -0
  124. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +19 -0
  125. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -0
  126. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +19 -0
  127. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -0
  128. package/lib/typescript/web-system/notification/index.d.ts +3 -0
  129. package/lib/typescript/web-system/notification/index.d.ts.map +1 -0
  130. package/package.json +1 -1
  131. package/src/api.ts +17 -0
  132. package/src/api.web.ts +7 -2
  133. package/src/events/types.ts +3 -4
  134. package/src/specs/NativeAudioAPIModule.ts +23 -7
  135. package/src/system/AudioManager.ts +10 -23
  136. package/src/system/index.ts +1 -0
  137. package/src/system/notification/PlaybackNotificationManager.ts +193 -0
  138. package/src/system/notification/RecordingNotificationManager.ts +242 -0
  139. package/src/system/notification/SimpleNotificationManager.ts +170 -0
  140. package/src/system/notification/index.ts +4 -0
  141. package/src/system/notification/types.ts +111 -0
  142. package/src/system/types.ts +0 -18
  143. package/src/types.ts +17 -17
  144. package/src/web-system/index.ts +1 -0
  145. package/src/web-system/notification/PlaybackNotificationManager.ts +60 -0
  146. package/src/web-system/notification/RecordingNotificationManager.ts +60 -0
  147. package/src/web-system/notification/index.ts +2 -0
  148. package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +0 -347
  149. package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +0 -273
  150. package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +0 -57
  151. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +0 -61
@@ -0,0 +1,303 @@
1
+ package com.swmansion.audioapi.system.notification
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.content.Context
8
+ import android.content.Intent
9
+ import android.content.IntentFilter
10
+ import android.graphics.Color
11
+ import android.os.Build
12
+ import android.util.Log
13
+ import androidx.core.app.NotificationCompat
14
+ import com.facebook.react.bridge.ReactApplicationContext
15
+ import com.facebook.react.bridge.ReadableMap
16
+ import com.swmansion.audioapi.AudioAPIModule
17
+ import java.lang.ref.WeakReference
18
+
19
+ /**
20
+ * RecordingNotification
21
+ *
22
+ * Simple notification for audio recording:
23
+ * - Shows recording status with red background when recording
24
+ * - Simple start/stop button with microphone icon
25
+ * - Is persistent and cannot be swiped away when recording
26
+ * - Notifies its dismissal via RecordingNotificationReceiver
27
+ */
28
+ class RecordingNotification(
29
+ private val reactContext: WeakReference<ReactApplicationContext>,
30
+ private val audioAPIModule: WeakReference<AudioAPIModule>,
31
+ private val notificationId: Int,
32
+ private val channelId: String,
33
+ ) : BaseNotification {
34
+ companion object {
35
+ private const val TAG = "RecordingNotification"
36
+ const val ACTION_START = "com.swmansion.audioapi.RECORDING_START"
37
+ const val ACTION_STOP = "com.swmansion.audioapi.RECORDING_STOP"
38
+ }
39
+
40
+ private var notificationBuilder: NotificationCompat.Builder? = null
41
+ private var isRecording: Boolean = false
42
+ private var title: String = "Audio Recording"
43
+ private var description: String = "Ready to record"
44
+ private var receiver: RecordingNotificationReceiver? = null
45
+ private var startEnabled: Boolean = true
46
+ private var stopEnabled: Boolean = true
47
+
48
+ override fun init(params: ReadableMap?): Notification {
49
+ val context = reactContext.get() ?: throw IllegalStateException("React context is null")
50
+
51
+ // Register broadcast receiver
52
+ registerReceiver()
53
+
54
+ // Create notification channel first
55
+ createNotificationChannel()
56
+
57
+ // Create notification builder
58
+ notificationBuilder =
59
+ NotificationCompat
60
+ .Builder(context, channelId)
61
+ .setSmallIcon(android.R.drawable.ic_btn_speak_now)
62
+ .setContentTitle(title)
63
+ .setContentText(description)
64
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
65
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
66
+ .setOngoing(false)
67
+ .setAutoCancel(false)
68
+
69
+ // Set content intent to open app
70
+ val packageName = context.packageName
71
+ val openAppIntent = context.packageManager.getLaunchIntentForPackage(packageName)
72
+ if (openAppIntent != null) {
73
+ val pendingIntent =
74
+ PendingIntent.getActivity(
75
+ context,
76
+ 0,
77
+ openAppIntent,
78
+ PendingIntent.FLAG_IMMUTABLE,
79
+ )
80
+ notificationBuilder?.setContentIntent(pendingIntent)
81
+ }
82
+
83
+ // Set delete intent to handle dismissal
84
+ val deleteIntent = Intent(RecordingNotificationReceiver.ACTION_NOTIFICATION_DISMISSED)
85
+ deleteIntent.setPackage(context.packageName)
86
+ val deletePendingIntent =
87
+ PendingIntent.getBroadcast(
88
+ context,
89
+ notificationId,
90
+ deleteIntent,
91
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
92
+ )
93
+ notificationBuilder?.setDeleteIntent(deletePendingIntent)
94
+
95
+ // Apply initial params if provided
96
+ if (params != null) {
97
+ update(params)
98
+ }
99
+
100
+ return buildNotification()
101
+ }
102
+
103
+ override fun reset() {
104
+ // Unregister receiver
105
+ unregisterReceiver()
106
+
107
+ // Reset state
108
+ title = "Audio Recording"
109
+ description = "Ready to record"
110
+ isRecording = false
111
+ notificationBuilder = null
112
+ }
113
+
114
+ override fun getNotificationId(): Int = notificationId
115
+
116
+ override fun getChannelId(): String = channelId
117
+
118
+ override fun update(options: ReadableMap?): Notification {
119
+ if (options == null) {
120
+ return buildNotification()
121
+ }
122
+
123
+ // Handle control enable/disable
124
+ if (options.hasKey("control") && options.hasKey("enabled")) {
125
+ val control = options.getString("control")
126
+ val enabled = options.getBoolean("enabled")
127
+ when (control) {
128
+ "start" -> startEnabled = enabled
129
+ "stop" -> stopEnabled = enabled
130
+ }
131
+ updateActions()
132
+ return buildNotification()
133
+ }
134
+
135
+ // Update metadata
136
+ if (options.hasKey("title")) {
137
+ title = options.getString("title") ?: "Audio Recording"
138
+ }
139
+
140
+ if (options.hasKey("description")) {
141
+ description = options.getString("description") ?: "Ready to record"
142
+ }
143
+
144
+ // Update recording state
145
+ if (options.hasKey("state")) {
146
+ when (options.getString("state")) {
147
+ "recording" -> isRecording = true
148
+ "stopped" -> isRecording = false
149
+ }
150
+ }
151
+
152
+ // Update notification content
153
+ val statusText =
154
+ description.ifEmpty {
155
+ if (isRecording) "Recording..." else "Ready to record"
156
+ }
157
+ notificationBuilder
158
+ ?.setContentTitle(title)
159
+ ?.setContentText(statusText)
160
+ ?.setOngoing(isRecording)
161
+
162
+ // Set red color when recording
163
+ if (isRecording) {
164
+ notificationBuilder
165
+ ?.setColor(Color.RED)
166
+ ?.setColorized(true)
167
+ } else {
168
+ notificationBuilder
169
+ ?.setColorized(false)
170
+ }
171
+
172
+ // Update action button
173
+ updateActions()
174
+
175
+ return buildNotification()
176
+ }
177
+
178
+ private fun buildNotification(): Notification =
179
+ notificationBuilder?.build()
180
+ ?: throw IllegalStateException("Notification not initialized. Call init() first.")
181
+
182
+ private fun updateActions() {
183
+ val context = reactContext.get() ?: return
184
+
185
+ // Clear existing actions
186
+ notificationBuilder?.clearActions()
187
+
188
+ // Add appropriate action based on recording state and enabled controls
189
+ // Note: Android shows text labels in collapsed view, icons only in expanded/Auto/Wear
190
+ if (isRecording && stopEnabled) {
191
+ // Show STOP button when recording
192
+ val stopIntent = Intent(ACTION_STOP)
193
+ stopIntent.setPackage(context.packageName)
194
+ val stopPendingIntent =
195
+ PendingIntent.getBroadcast(
196
+ context,
197
+ 1001,
198
+ stopIntent,
199
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
200
+ )
201
+ val stopAction =
202
+ NotificationCompat.Action
203
+ .Builder(
204
+ android.R.drawable.ic_delete,
205
+ "Stop",
206
+ stopPendingIntent,
207
+ ).build()
208
+ notificationBuilder?.addAction(stopAction)
209
+ } else if (!isRecording && startEnabled) {
210
+ // Show START button when not recording
211
+ val startIntent = Intent(ACTION_START)
212
+ startIntent.setPackage(context.packageName)
213
+ val startPendingIntent =
214
+ PendingIntent.getBroadcast(
215
+ context,
216
+ 1000,
217
+ startIntent,
218
+ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
219
+ )
220
+ val startAction =
221
+ NotificationCompat.Action
222
+ .Builder(
223
+ android.R.drawable.ic_btn_speak_now,
224
+ "Record",
225
+ startPendingIntent,
226
+ ).build()
227
+ notificationBuilder?.addAction(startAction)
228
+ }
229
+
230
+ // Use BigTextStyle to ensure actions are visible
231
+ val statusText =
232
+ description.ifEmpty {
233
+ if (isRecording) "Recording in progress..." else "Ready to record"
234
+ }
235
+ notificationBuilder?.setStyle(
236
+ NotificationCompat
237
+ .BigTextStyle()
238
+ .bigText(statusText),
239
+ )
240
+ }
241
+
242
+ private fun createNotificationChannel() {
243
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
244
+ val context = reactContext.get() ?: return
245
+
246
+ val channel =
247
+ NotificationChannel(
248
+ channelId,
249
+ "Audio Recording",
250
+ NotificationManager.IMPORTANCE_HIGH,
251
+ ).apply {
252
+ description = "Recording controls and status"
253
+ setShowBadge(true)
254
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
255
+ enableLights(true)
256
+ lightColor = Color.RED
257
+ enableVibration(false)
258
+ }
259
+
260
+ val notificationManager =
261
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
262
+ notificationManager.createNotificationChannel(channel)
263
+
264
+ Log.d(TAG, "Notification channel created: $channelId")
265
+ }
266
+ }
267
+
268
+ private fun registerReceiver() {
269
+ val context = reactContext.get() ?: return
270
+
271
+ if (receiver == null) {
272
+ receiver = RecordingNotificationReceiver()
273
+ RecordingNotificationReceiver.setAudioAPIModule(audioAPIModule.get())
274
+
275
+ val filter = IntentFilter()
276
+ filter.addAction(ACTION_START)
277
+ filter.addAction(ACTION_STOP)
278
+ filter.addAction(RecordingNotificationReceiver.ACTION_NOTIFICATION_DISMISSED)
279
+
280
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
281
+ context.registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED)
282
+ } else {
283
+ context.registerReceiver(receiver, filter)
284
+ }
285
+
286
+ Log.d(TAG, "RecordingNotificationReceiver registered")
287
+ }
288
+ }
289
+
290
+ private fun unregisterReceiver() {
291
+ val context = reactContext.get() ?: return
292
+
293
+ receiver?.let {
294
+ try {
295
+ context.unregisterReceiver(it)
296
+ receiver = null
297
+ Log.d(TAG, "RecordingNotificationReceiver unregistered")
298
+ } catch (e: Exception) {
299
+ Log.e(TAG, "Error unregistering receiver: ${e.message}", e)
300
+ }
301
+ }
302
+ }
303
+ }
@@ -0,0 +1,45 @@
1
+ package com.swmansion.audioapi.system.notification
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.util.Log
7
+ import com.swmansion.audioapi.AudioAPIModule
8
+
9
+ /**
10
+ * Broadcast receiver for handling recording notification actions and dismissal.
11
+ */
12
+ class RecordingNotificationReceiver : BroadcastReceiver() {
13
+ companion object {
14
+ const val ACTION_NOTIFICATION_DISMISSED = "com.swmansion.audioapi.RECORDING_NOTIFICATION_DISMISSED"
15
+ private const val TAG = "RecordingNotificationReceiver"
16
+
17
+ private var audioAPIModule: AudioAPIModule? = null
18
+
19
+ fun setAudioAPIModule(module: AudioAPIModule?) {
20
+ audioAPIModule = module
21
+ }
22
+ }
23
+
24
+ override fun onReceive(
25
+ context: Context?,
26
+ intent: Intent?,
27
+ ) {
28
+ when (intent?.action) {
29
+ ACTION_NOTIFICATION_DISMISSED -> {
30
+ Log.d(TAG, "Recording notification dismissed by user")
31
+ audioAPIModule?.invokeHandlerWithEventNameAndEventBody("recordingNotificationDismissed", mapOf())
32
+ }
33
+
34
+ RecordingNotification.ACTION_START -> {
35
+ Log.d(TAG, "Start recording action received")
36
+ audioAPIModule?.invokeHandlerWithEventNameAndEventBody("recordingNotificationStart", mapOf())
37
+ }
38
+
39
+ RecordingNotification.ACTION_STOP -> {
40
+ Log.d(TAG, "Stop recording action received")
41
+ audioAPIModule?.invokeHandlerWithEventNameAndEventBody("recordingNotificationStop", mapOf())
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,119 @@
1
+ package com.swmansion.audioapi.system.notification
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.content.Context
8
+ import android.os.Build
9
+ import androidx.core.app.NotificationCompat
10
+ import com.facebook.react.bridge.ReactApplicationContext
11
+ import com.facebook.react.bridge.ReadableMap
12
+ import com.swmansion.audioapi.R
13
+ import java.lang.ref.WeakReference
14
+
15
+ /**
16
+ * This serves as a reference implementation and starting point for more complex notifications.
17
+ */
18
+ class SimpleNotification(
19
+ private val reactContext: WeakReference<ReactApplicationContext>,
20
+ private val notificationId: Int = DEFAULT_NOTIFICATION_ID,
21
+ ) : BaseNotification {
22
+ companion object {
23
+ const val DEFAULT_NOTIFICATION_ID = 200
24
+ const val CHANNEL_ID = "react-native-audio-api-simple"
25
+ const val CHANNEL_NAME = "Simple Notifications"
26
+ }
27
+
28
+ private var title: String = "Audio Playing"
29
+ private var text: String = ""
30
+
31
+ init {
32
+ createNotificationChannel()
33
+ }
34
+
35
+ override fun init(options: ReadableMap?): Notification {
36
+ // Parse options from JavaScript
37
+ if (options != null) {
38
+ if (options.hasKey("title")) {
39
+ title = options.getString("title") ?: "Audio Playing"
40
+ }
41
+ if (options.hasKey("text")) {
42
+ text = options.getString("text") ?: ""
43
+ }
44
+ }
45
+
46
+ return buildNotification()
47
+ }
48
+
49
+ override fun update(options: ReadableMap?): Notification {
50
+ // Update works the same as init for simple notifications
51
+ return init(options)
52
+ }
53
+
54
+ override fun reset() {
55
+ // Reset to default values
56
+ title = "Audio Playing"
57
+ text = ""
58
+ }
59
+
60
+ override fun getNotificationId(): Int = notificationId
61
+
62
+ override fun getChannelId(): String = CHANNEL_ID
63
+
64
+ private fun buildNotification(): Notification {
65
+ val context = reactContext.get() ?: throw IllegalStateException("React context is null")
66
+
67
+ // Use a system icon that's guaranteed to exist
68
+ val icon = android.R.drawable.ic_dialog_info
69
+
70
+ val builder =
71
+ NotificationCompat
72
+ .Builder(context, CHANNEL_ID)
73
+ .setContentTitle(title)
74
+ .setContentText(text)
75
+ .setSmallIcon(icon)
76
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
77
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
78
+ .setOngoing(false)
79
+ .setAutoCancel(false)
80
+
81
+ // Add content intent to open the app when notification is tapped
82
+ val packageName = context.packageName
83
+ val openAppIntent = context.packageManager?.getLaunchIntentForPackage(packageName)
84
+ if (openAppIntent != null) {
85
+ val pendingIntent =
86
+ PendingIntent.getActivity(
87
+ context,
88
+ 0,
89
+ openAppIntent,
90
+ PendingIntent.FLAG_IMMUTABLE,
91
+ )
92
+ builder.setContentIntent(pendingIntent)
93
+ }
94
+
95
+ return builder.build()
96
+ }
97
+
98
+ private fun createNotificationChannel() {
99
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
100
+ val context = reactContext.get() ?: return
101
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
102
+
103
+ val channel =
104
+ NotificationChannel(
105
+ CHANNEL_ID,
106
+ CHANNEL_NAME,
107
+ NotificationManager.IMPORTANCE_HIGH,
108
+ ).apply {
109
+ description = "Simple notification channel for audio playback"
110
+ setShowBadge(true)
111
+ lockscreenVisibility = Notification.VISIBILITY_PUBLIC
112
+ enableLights(true)
113
+ enableVibration(false)
114
+ }
115
+
116
+ notificationManager.createNotificationChannel(channel)
117
+ }
118
+ }
119
+ }
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include <audioapi/utils/Result.hpp>
4
+ #include <atomic>
4
5
  #include <memory>
5
6
  #include <string>
6
7
  #include <tuple>
@@ -1,6 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include <audioapi/utils/Result.hpp>
4
+ #include <atomic>
4
5
  #include <memory>
5
6
  #include <string>
6
7
  #include <vector>
@@ -15,30 +15,30 @@ namespace audioapi {
15
15
 
16
16
  class AudioFileProperties {
17
17
  public:
18
- enum class Format {
19
- WAV = 1,
20
- CAF = 2,
21
- M4A = 3,
22
- FLAC = 4,
18
+ enum class FileDirectory {
19
+ Document = 0,
20
+ Cache = 1,
23
21
  };
24
22
 
25
- enum class BitDepth {
26
- Bit16 = 1,
27
- Bit24 = 2,
28
- Bit32 = 3,
23
+ enum class Format {
24
+ WAV = 0,
25
+ CAF = 1,
26
+ M4A = 2,
27
+ FLAC = 3,
29
28
  };
30
29
 
31
30
  enum class IOSAudioQuality {
32
- Min = 1,
33
- Low = 2,
34
- Medium = 3,
35
- High = 4,
36
- Max = 5,
31
+ Min = 0,
32
+ Low = 1,
33
+ Medium = 2,
34
+ High = 3,
35
+ Max = 4,
37
36
  };
38
37
 
39
- enum class FileDirectory {
40
- Document = 1,
41
- Cache = 2,
38
+ enum class BitDepth {
39
+ Bit16 = 0,
40
+ Bit24 = 1,
41
+ Bit32 = 2,
42
42
  };
43
43
 
44
44
  AudioFileProperties(
@@ -11,7 +11,7 @@
11
11
  @class AudioEngine;
12
12
  @class NotificationManager;
13
13
  @class AudioSessionManager;
14
- @class LockScreenManager;
14
+ @class NotificationRegistry;
15
15
 
16
16
  @interface AudioAPIModule : RCTEventEmitter
17
17
  #ifdef RCT_NEW_ARCH_ENABLED
@@ -23,7 +23,7 @@
23
23
  @property (nonatomic, strong) AudioEngine *audioEngine;
24
24
  @property (nonatomic, strong) NotificationManager *notificationManager;
25
25
  @property (nonatomic, strong) AudioSessionManager *audioSessionManager;
26
- @property (nonatomic, strong) LockScreenManager *lockScreenManager;
26
+ @property (nonatomic, strong) NotificationRegistry *notificationRegistry;
27
27
 
28
28
  - (void)invokeHandlerWithEventName:(NSString *)eventName eventBody:(NSDictionary *)eventBody;
29
29