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.
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +34 -6
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +4 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/ptrs.hpp +8 -0
- package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/utils.cpp +4 -0
- package/android/src/main/cpp/audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h +1 -0
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +164 -16
- package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioPlayer.kt +10 -8
- package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioRecorder.kt +10 -8
- package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +3 -4
- package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +128 -0
- package/android/src/main/java/com/swmansion/audioapi/system/ForegroundServiceManager.kt +116 -0
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +115 -107
- package/android/src/main/java/com/swmansion/audioapi/system/PermissionRequestListener.kt +2 -1
- package/android/src/main/java/com/swmansion/audioapi/system/notification/BaseNotification.kt +47 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/NotificationRegistry.kt +191 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +669 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt +33 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotification.kt +303 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +45 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt +119 -0
- package/common/cpp/audioapi/core/utils/AudioFileWriter.h +1 -0
- package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +1 -0
- package/common/cpp/audioapi/utils/AudioFileProperties.h +17 -17
- package/ios/audioapi/ios/AudioAPIModule.h +2 -2
- package/ios/audioapi/ios/AudioAPIModule.mm +108 -18
- package/ios/audioapi/ios/core/IOSAudioRecorder.mm +8 -7
- package/ios/audioapi/ios/core/NativeAudioPlayer.m +1 -1
- package/ios/audioapi/ios/core/NativeAudioRecorder.m +9 -2
- package/ios/audioapi/ios/system/AudioEngine.h +2 -0
- package/ios/audioapi/ios/system/AudioEngine.mm +49 -6
- package/ios/audioapi/ios/system/AudioSessionManager.mm +12 -9
- package/ios/audioapi/ios/system/NotificationManager.mm +7 -4
- package/ios/audioapi/ios/system/notification/BaseNotification.h +58 -0
- package/ios/audioapi/ios/system/notification/NotificationRegistry.h +70 -0
- package/ios/audioapi/ios/system/notification/NotificationRegistry.mm +172 -0
- package/ios/audioapi/ios/system/notification/PlaybackNotification.h +27 -0
- package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +427 -0
- package/lib/commonjs/api.js +72 -1
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/api.web.js +27 -14
- package/lib/commonjs/api.web.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/commonjs/system/AudioManager.js +6 -9
- package/lib/commonjs/system/AudioManager.js.map +1 -1
- package/lib/commonjs/system/index.js +13 -0
- package/lib/commonjs/system/index.js.map +1 -1
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js +135 -0
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/commonjs/system/notification/RecordingNotificationManager.js +182 -0
- package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/commonjs/system/notification/SimpleNotificationManager.js +122 -0
- package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -0
- package/lib/commonjs/system/notification/index.js +45 -0
- package/lib/commonjs/system/notification/index.js.map +1 -0
- package/lib/commonjs/system/notification/types.js +6 -0
- package/lib/commonjs/system/notification/types.js.map +1 -0
- package/lib/commonjs/types.js +17 -17
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/web-system/index.js +17 -0
- package/lib/commonjs/web-system/index.js.map +1 -0
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +34 -0
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +34 -0
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/commonjs/web-system/notification/index.js +21 -0
- package/lib/commonjs/web-system/notification/index.js.map +1 -0
- package/lib/module/api.js +4 -0
- package/lib/module/api.js.map +1 -1
- package/lib/module/api.web.js +3 -1
- package/lib/module/api.web.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/module/system/AudioManager.js +6 -9
- package/lib/module/system/AudioManager.js.map +1 -1
- package/lib/module/system/index.js +1 -0
- package/lib/module/system/index.js.map +1 -1
- package/lib/module/system/notification/PlaybackNotificationManager.js +131 -0
- package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/module/system/notification/RecordingNotificationManager.js +178 -0
- package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/module/system/notification/SimpleNotificationManager.js +118 -0
- package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -0
- package/lib/module/system/notification/index.js +7 -0
- package/lib/module/system/notification/index.js.map +1 -0
- package/lib/module/system/notification/types.js +4 -0
- package/lib/module/system/notification/types.js.map +1 -0
- package/lib/module/types.js +17 -17
- package/lib/module/types.js.map +1 -1
- package/lib/module/web-system/index.js +4 -0
- package/lib/module/web-system/index.js.map +1 -0
- package/lib/module/web-system/notification/PlaybackNotificationManager.js +30 -0
- package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -0
- package/lib/module/web-system/notification/RecordingNotificationManager.js +30 -0
- package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -0
- package/lib/module/web-system/notification/index.js +5 -0
- package/lib/module/web-system/notification/index.js.map +1 -0
- package/lib/typescript/api.d.ts +2 -0
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/api.web.d.ts +3 -1
- package/lib/typescript/api.web.d.ts.map +1 -1
- package/lib/typescript/events/types.d.ts +3 -3
- package/lib/typescript/events/types.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts +16 -5
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/system/AudioManager.d.ts +4 -5
- package/lib/typescript/system/AudioManager.d.ts.map +1 -1
- package/lib/typescript/system/index.d.ts +1 -0
- package/lib/typescript/system/index.d.ts.map +1 -1
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +22 -0
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -0
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +23 -0
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -0
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +20 -0
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -0
- package/lib/typescript/system/notification/index.d.ts +5 -0
- package/lib/typescript/system/notification/index.d.ts.map +1 -0
- package/lib/typescript/system/notification/types.d.ts +65 -0
- package/lib/typescript/system/notification/types.d.ts.map +1 -0
- package/lib/typescript/system/types.d.ts +0 -16
- package/lib/typescript/system/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +16 -16
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-system/index.d.ts +2 -0
- package/lib/typescript/web-system/index.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +19 -0
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +19 -0
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -0
- package/lib/typescript/web-system/notification/index.d.ts +3 -0
- package/lib/typescript/web-system/notification/index.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/api.ts +17 -0
- package/src/api.web.ts +7 -2
- package/src/events/types.ts +3 -4
- package/src/specs/NativeAudioAPIModule.ts +23 -7
- package/src/system/AudioManager.ts +10 -23
- package/src/system/index.ts +1 -0
- package/src/system/notification/PlaybackNotificationManager.ts +193 -0
- package/src/system/notification/RecordingNotificationManager.ts +242 -0
- package/src/system/notification/SimpleNotificationManager.ts +170 -0
- package/src/system/notification/index.ts +4 -0
- package/src/system/notification/types.ts +111 -0
- package/src/system/types.ts +0 -18
- package/src/types.ts +17 -17
- package/src/web-system/index.ts +1 -0
- package/src/web-system/notification/PlaybackNotificationManager.ts +60 -0
- package/src/web-system/notification/RecordingNotificationManager.ts +60 -0
- package/src/web-system/notification/index.ts +2 -0
- package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +0 -347
- package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +0 -273
- package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +0 -57
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +0 -61
package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotification.kt
ADDED
|
@@ -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
|
+
}
|
package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt
ADDED
|
@@ -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
|
+
}
|
|
@@ -15,30 +15,30 @@ namespace audioapi {
|
|
|
15
15
|
|
|
16
16
|
class AudioFileProperties {
|
|
17
17
|
public:
|
|
18
|
-
enum class
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
M4A = 3,
|
|
22
|
-
FLAC = 4,
|
|
18
|
+
enum class FileDirectory {
|
|
19
|
+
Document = 0,
|
|
20
|
+
Cache = 1,
|
|
23
21
|
};
|
|
24
22
|
|
|
25
|
-
enum class
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 =
|
|
33
|
-
Low =
|
|
34
|
-
Medium =
|
|
35
|
-
High =
|
|
36
|
-
Max =
|
|
31
|
+
Min = 0,
|
|
32
|
+
Low = 1,
|
|
33
|
+
Medium = 2,
|
|
34
|
+
High = 3,
|
|
35
|
+
Max = 4,
|
|
37
36
|
};
|
|
38
37
|
|
|
39
|
-
enum class
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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)
|
|
26
|
+
@property (nonatomic, strong) NotificationRegistry *notificationRegistry;
|
|
27
27
|
|
|
28
28
|
- (void)invokeHandlerWithEventName:(NSString *)eventName eventBody:(NSDictionary *)eventBody;
|
|
29
29
|
|