react-native-audio-api 0.11.0-nightly-db51488-20251208 → 0.11.0-nightly-6ba0571-20251209
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/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 +8 -23
- package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +127 -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 +668 -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 +43 -0
- package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt +119 -0
- package/ios/audioapi/ios/AudioAPIModule.h +2 -2
- package/ios/audioapi/ios/AudioAPIModule.mm +108 -18
- package/ios/audioapi/ios/system/AudioEngine.mm +2 -2
- package/ios/audioapi/ios/system/AudioSessionManager.mm +1 -1
- package/ios/audioapi/ios/system/NotificationManager.mm +1 -1
- 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 +59 -10
- 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/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 +5 -1
- 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/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 +3 -1
- 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 +4 -18
- 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/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 -2
- package/src/api.web.ts +7 -2
- package/src/events/types.ts +4 -20
- 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 +110 -0
- package/src/system/types.ts +0 -18
- 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/ios/audioapi/ios/system/LockScreenManager.h +0 -23
- package/ios/audioapi/ios/system/LockScreenManager.mm +0 -314
|
@@ -4,13 +4,11 @@ import android.Manifest
|
|
|
4
4
|
import android.app.NotificationChannel
|
|
5
5
|
import android.app.NotificationManager
|
|
6
6
|
import android.content.Context
|
|
7
|
-
import android.content.Intent
|
|
8
7
|
import android.content.IntentFilter
|
|
9
8
|
import android.content.pm.PackageManager
|
|
10
9
|
import android.media.AudioDeviceInfo
|
|
11
10
|
import android.media.AudioManager
|
|
12
11
|
import android.os.Build
|
|
13
|
-
import android.support.v4.media.session.MediaSessionCompat
|
|
14
12
|
import androidx.annotation.RequiresApi
|
|
15
13
|
import androidx.core.app.NotificationCompat
|
|
16
14
|
import androidx.core.content.ContextCompat
|
|
@@ -20,11 +18,14 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
20
18
|
import com.facebook.react.modules.core.PermissionAwareActivity
|
|
21
19
|
import com.facebook.react.modules.core.PermissionListener
|
|
22
20
|
import com.swmansion.audioapi.AudioAPIModule
|
|
23
|
-
import com.swmansion.audioapi.core.NativeAudioPlayer
|
|
24
|
-
import com.swmansion.audioapi.core.NativeAudioRecorder
|
|
25
21
|
import com.swmansion.audioapi.system.PermissionRequestListener.Companion.RECORDING_REQUEST_CODE
|
|
22
|
+
import com.swmansion.audioapi.system.notification.NotificationRegistry
|
|
23
|
+
import com.swmansion.audioapi.system.notification.PlaybackNotification
|
|
24
|
+
import com.swmansion.audioapi.system.notification.PlaybackNotificationReceiver
|
|
25
|
+
import com.swmansion.audioapi.system.notification.RecordingNotification
|
|
26
|
+
import com.swmansion.audioapi.system.notification.RecordingNotificationReceiver
|
|
27
|
+
import com.swmansion.audioapi.system.notification.SimpleNotification
|
|
26
28
|
import java.lang.ref.WeakReference
|
|
27
|
-
import java.util.UUID
|
|
28
29
|
|
|
29
30
|
object MediaSessionManager {
|
|
30
31
|
private lateinit var audioAPIModule: WeakReference<AudioAPIModule>
|
|
@@ -33,17 +34,13 @@ object MediaSessionManager {
|
|
|
33
34
|
const val CHANNEL_ID = "react-native-audio-api"
|
|
34
35
|
|
|
35
36
|
private lateinit var audioManager: AudioManager
|
|
36
|
-
private lateinit var mediaSession: MediaSessionCompat
|
|
37
|
-
lateinit var mediaNotificationManager: MediaNotificationManager
|
|
38
|
-
private lateinit var lockScreenManager: LockScreenManager
|
|
39
37
|
private lateinit var audioFocusListener: AudioFocusListener
|
|
40
38
|
private lateinit var volumeChangeListener: VolumeChangeListener
|
|
41
|
-
private lateinit var
|
|
39
|
+
private lateinit var playbackNotificationReceiver: PlaybackNotificationReceiver
|
|
40
|
+
private lateinit var recordingNotificationReceiver: RecordingNotificationReceiver
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
private val nativeAudioPlayers = mutableMapOf<String, NativeAudioPlayer>()
|
|
46
|
-
private val nativeAudioRecorders = mutableMapOf<String, NativeAudioRecorder>()
|
|
42
|
+
// New notification system
|
|
43
|
+
private lateinit var notificationRegistry: NotificationRegistry
|
|
47
44
|
|
|
48
45
|
fun initialize(
|
|
49
46
|
audioAPIModule: WeakReference<AudioAPIModule>,
|
|
@@ -52,118 +49,54 @@ object MediaSessionManager {
|
|
|
52
49
|
this.audioAPIModule = audioAPIModule
|
|
53
50
|
this.reactContext = reactContext
|
|
54
51
|
this.audioManager = reactContext.get()?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
55
|
-
|
|
52
|
+
|
|
53
|
+
// Initialize ForegroundServiceManager
|
|
54
|
+
ForegroundServiceManager.initialize(reactContext)
|
|
56
55
|
|
|
57
56
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
58
57
|
createChannel()
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
MediaReceiver(this.reactContext, WeakReference(this.mediaSession), WeakReference(this.mediaNotificationManager), this.audioAPIModule)
|
|
65
|
-
this.mediaSession.setCallback(MediaSessionCallback(this.audioAPIModule, WeakReference(this.mediaNotificationManager)))
|
|
66
|
-
|
|
67
|
-
val filter = IntentFilter()
|
|
68
|
-
filter.addAction(MediaNotificationManager.REMOVE_NOTIFICATION)
|
|
69
|
-
filter.addAction(MediaNotificationManager.MEDIA_BUTTON)
|
|
70
|
-
filter.addAction(Intent.ACTION_MEDIA_BUTTON)
|
|
71
|
-
filter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
|
|
60
|
+
// Set up PlaybackNotificationReceiver
|
|
61
|
+
PlaybackNotificationReceiver.setAudioAPIModule(audioAPIModule.get())
|
|
62
|
+
this.playbackNotificationReceiver = PlaybackNotificationReceiver()
|
|
72
63
|
|
|
64
|
+
// Register PlaybackNotificationReceiver
|
|
65
|
+
val playbackFilter = IntentFilter(PlaybackNotificationReceiver.ACTION_NOTIFICATION_DISMISSED)
|
|
73
66
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
74
|
-
this.reactContext.get()!!.registerReceiver(
|
|
67
|
+
this.reactContext.get()!!.registerReceiver(playbackNotificationReceiver, playbackFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
75
68
|
} else {
|
|
76
69
|
ContextCompat.registerReceiver(
|
|
77
70
|
this.reactContext.get()!!,
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
playbackNotificationReceiver,
|
|
72
|
+
playbackFilter,
|
|
80
73
|
ContextCompat.RECEIVER_NOT_EXPORTED,
|
|
81
74
|
)
|
|
82
75
|
}
|
|
83
76
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
fun attachAudioPlayer(player: NativeAudioPlayer): String {
|
|
90
|
-
val uuid = UUID.randomUUID().toString()
|
|
91
|
-
nativeAudioPlayers[uuid] = player
|
|
92
|
-
|
|
93
|
-
return uuid
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
fun detachAudioPlayer(uuid: String) {
|
|
97
|
-
nativeAudioPlayers.remove(uuid)
|
|
98
|
-
}
|
|
77
|
+
// Set up RecordingNotificationReceiver
|
|
78
|
+
RecordingNotificationReceiver.setAudioAPIModule(audioAPIModule.get())
|
|
79
|
+
this.recordingNotificationReceiver = RecordingNotificationReceiver()
|
|
99
80
|
|
|
100
|
-
|
|
101
|
-
val
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
fun startForegroundServiceIfNecessary() {
|
|
112
|
-
if (nativeAudioPlayers.isNotEmpty() || nativeAudioRecorders.isNotEmpty()) {
|
|
113
|
-
startForegroundService()
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
fun stopForegroundServiceIfNecessary() {
|
|
118
|
-
if (nativeAudioPlayers.isEmpty() && nativeAudioRecorders.isEmpty()) {
|
|
119
|
-
stopForegroundService()
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private fun startForegroundService() {
|
|
124
|
-
synchronized(serviceStateLock) {
|
|
125
|
-
if (isServiceRunning || reactContext.get() == null) {
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
val intent = Intent(reactContext.get(), MediaNotificationManager.AudioForegroundService::class.java)
|
|
130
|
-
intent.action = MediaNotificationManager.ForegroundAction.START_FOREGROUND.name
|
|
131
|
-
|
|
132
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
133
|
-
ContextCompat.startForegroundService(reactContext.get()!!, intent)
|
|
134
|
-
} else {
|
|
135
|
-
reactContext.get()!!.startService(intent)
|
|
136
|
-
}
|
|
137
|
-
isServiceRunning = true
|
|
81
|
+
// Register RecordingNotificationReceiver
|
|
82
|
+
val recordingFilter = IntentFilter(RecordingNotificationReceiver.ACTION_NOTIFICATION_DISMISSED)
|
|
83
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
84
|
+
this.reactContext.get()!!.registerReceiver(recordingNotificationReceiver, recordingFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
85
|
+
} else {
|
|
86
|
+
ContextCompat.registerReceiver(
|
|
87
|
+
this.reactContext.get()!!,
|
|
88
|
+
recordingNotificationReceiver,
|
|
89
|
+
recordingFilter,
|
|
90
|
+
ContextCompat.RECEIVER_NOT_EXPORTED,
|
|
91
|
+
)
|
|
138
92
|
}
|
|
139
|
-
}
|
|
140
93
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
return
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
val intent = Intent(reactContext.get(), MediaNotificationManager.AudioForegroundService::class.java)
|
|
148
|
-
intent.action = MediaNotificationManager.ForegroundAction.STOP_FOREGROUND.name
|
|
149
|
-
reactContext.get()!!.startService(intent)
|
|
150
|
-
isServiceRunning = false
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
fun setLockScreenInfo(info: ReadableMap?) {
|
|
155
|
-
lockScreenManager.setLockScreenInfo(info)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
fun resetLockScreenInfo() {
|
|
159
|
-
lockScreenManager.resetLockScreenInfo()
|
|
160
|
-
}
|
|
94
|
+
this.audioFocusListener =
|
|
95
|
+
AudioFocusListener(WeakReference(this.audioManager), this.audioAPIModule)
|
|
96
|
+
this.volumeChangeListener = VolumeChangeListener(WeakReference(this.audioManager), this.audioAPIModule)
|
|
161
97
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
enabled: Boolean,
|
|
165
|
-
) {
|
|
166
|
-
lockScreenManager.enableRemoteCommand(name, enabled)
|
|
98
|
+
// Initialize new notification system
|
|
99
|
+
this.notificationRegistry = NotificationRegistry(this.reactContext)
|
|
167
100
|
}
|
|
168
101
|
|
|
169
102
|
fun getDevicePreferredSampleRate(): Double {
|
|
@@ -211,6 +144,41 @@ object MediaSessionManager {
|
|
|
211
144
|
"Denied"
|
|
212
145
|
}
|
|
213
146
|
|
|
147
|
+
fun requestNotificationPermissions(permissionListener: PermissionListener) {
|
|
148
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
149
|
+
val permissionAwareActivity = reactContext.get()!!.currentActivity as PermissionAwareActivity
|
|
150
|
+
permissionAwareActivity.requestPermissions(
|
|
151
|
+
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
|
152
|
+
PermissionRequestListener.NOTIFICATION_REQUEST_CODE,
|
|
153
|
+
permissionListener,
|
|
154
|
+
)
|
|
155
|
+
} else {
|
|
156
|
+
// For Android < 13, permission is granted by default
|
|
157
|
+
val result = Arguments.createMap()
|
|
158
|
+
result.putString("status", "Granted")
|
|
159
|
+
permissionListener.onRequestPermissionsResult(
|
|
160
|
+
PermissionRequestListener.NOTIFICATION_REQUEST_CODE,
|
|
161
|
+
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
|
162
|
+
intArrayOf(PackageManager.PERMISSION_GRANTED),
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fun checkNotificationPermissions(): String {
|
|
168
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
169
|
+
return if (reactContext.get()!!.checkSelfPermission(
|
|
170
|
+
Manifest.permission.POST_NOTIFICATIONS,
|
|
171
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
172
|
+
) {
|
|
173
|
+
"Granted"
|
|
174
|
+
} else {
|
|
175
|
+
"Denied"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// For Android < 13, permission is granted by default
|
|
179
|
+
return "Granted"
|
|
180
|
+
}
|
|
181
|
+
|
|
214
182
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
215
183
|
private fun createChannel() {
|
|
216
184
|
val notificationManager =
|
|
@@ -267,4 +235,44 @@ object MediaSessionManager {
|
|
|
267
235
|
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "Bluetooth SCO"
|
|
268
236
|
else -> "Other (${device.type})"
|
|
269
237
|
}
|
|
238
|
+
|
|
239
|
+
// New notification system methods
|
|
240
|
+
fun registerNotification(
|
|
241
|
+
type: String,
|
|
242
|
+
key: String,
|
|
243
|
+
) {
|
|
244
|
+
val notification =
|
|
245
|
+
when (type) {
|
|
246
|
+
"simple" -> SimpleNotification(reactContext)
|
|
247
|
+
"playback" -> PlaybackNotification(reactContext, audioAPIModule, 100, "audio_playback")
|
|
248
|
+
"recording" -> RecordingNotification(reactContext, audioAPIModule, 101, "audio_recording23")
|
|
249
|
+
else -> throw IllegalArgumentException("Unknown notification type: $type")
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
notificationRegistry.registerNotification(key, notification)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
fun showNotification(
|
|
256
|
+
key: String,
|
|
257
|
+
options: ReadableMap?,
|
|
258
|
+
) {
|
|
259
|
+
notificationRegistry.showNotification(key, options)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fun updateNotification(
|
|
263
|
+
key: String,
|
|
264
|
+
options: ReadableMap?,
|
|
265
|
+
) {
|
|
266
|
+
notificationRegistry.updateNotification(key, options)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
fun hideNotification(key: String) {
|
|
270
|
+
notificationRegistry.hideNotification(key)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fun unregisterNotification(key: String) {
|
|
274
|
+
notificationRegistry.unregisterNotification(key)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fun isNotificationActive(key: String): Boolean = notificationRegistry.isNotificationActive(key)
|
|
270
278
|
}
|
|
@@ -9,6 +9,7 @@ class PermissionRequestListener(
|
|
|
9
9
|
) : PermissionListener {
|
|
10
10
|
companion object {
|
|
11
11
|
const val RECORDING_REQUEST_CODE = 1234
|
|
12
|
+
const val NOTIFICATION_REQUEST_CODE = 1235
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
override fun onRequestPermissionsResult(
|
|
@@ -16,7 +17,7 @@ class PermissionRequestListener(
|
|
|
16
17
|
permissions: Array<String>,
|
|
17
18
|
grantResults: IntArray,
|
|
18
19
|
): Boolean {
|
|
19
|
-
if (requestCode == RECORDING_REQUEST_CODE) {
|
|
20
|
+
if (requestCode == RECORDING_REQUEST_CODE || requestCode == NOTIFICATION_REQUEST_CODE) {
|
|
20
21
|
if (grantResults.isEmpty()) {
|
|
21
22
|
this.promise.resolve("Undetermined")
|
|
22
23
|
} else {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
package com.swmansion.audioapi.system.notification
|
|
2
|
+
|
|
3
|
+
import android.app.Notification
|
|
4
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base interface for all notification types.
|
|
8
|
+
* Implementations should handle their own notification channel creation,
|
|
9
|
+
* notification building, and lifecycle management.
|
|
10
|
+
*/
|
|
11
|
+
interface BaseNotification {
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the notification with the provided options.
|
|
14
|
+
* This method should create the notification and prepare it for display.
|
|
15
|
+
*
|
|
16
|
+
* @param options Configuration options from JavaScript side
|
|
17
|
+
* @return The built Notification ready to be shown
|
|
18
|
+
*/
|
|
19
|
+
fun init(options: ReadableMap?): Notification
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Update the notification with new options.
|
|
23
|
+
* This method should rebuild the notification with updated data.
|
|
24
|
+
*
|
|
25
|
+
* @param options New configuration options from JavaScript side
|
|
26
|
+
* @return The updated Notification ready to be shown
|
|
27
|
+
*/
|
|
28
|
+
fun update(options: ReadableMap?): Notification
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Reset the notification to its initial state.
|
|
32
|
+
* This should clear any stored data and stop any ongoing processes.
|
|
33
|
+
*/
|
|
34
|
+
fun reset()
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the unique ID for this notification.
|
|
38
|
+
* Used by the NotificationManager to track and manage notifications.
|
|
39
|
+
*/
|
|
40
|
+
fun getNotificationId(): Int
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the channel ID for this notification.
|
|
44
|
+
* Required for Android O+ notification channels.
|
|
45
|
+
*/
|
|
46
|
+
fun getChannelId(): String
|
|
47
|
+
}
|
package/android/src/main/java/com/swmansion/audioapi/system/notification/NotificationRegistry.kt
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
package com.swmansion.audioapi.system.notification
|
|
2
|
+
|
|
3
|
+
import android.app.Notification
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import androidx.core.app.NotificationManagerCompat
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.bridge.ReadableMap
|
|
8
|
+
import com.swmansion.audioapi.system.ForegroundServiceManager
|
|
9
|
+
import java.lang.ref.WeakReference
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Central notification registry that manages multiple notification instances.
|
|
13
|
+
*/
|
|
14
|
+
class NotificationRegistry(
|
|
15
|
+
private val reactContext: WeakReference<ReactApplicationContext>,
|
|
16
|
+
) {
|
|
17
|
+
companion object {
|
|
18
|
+
private const val TAG = "NotificationRegistry"
|
|
19
|
+
|
|
20
|
+
// Store last built notifications for foreground service access
|
|
21
|
+
private val builtNotifications = mutableMapOf<Int, Notification>()
|
|
22
|
+
|
|
23
|
+
fun getBuiltNotification(notificationId: Int): Notification? = builtNotifications[notificationId]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private val notifications = mutableMapOf<String, BaseNotification>()
|
|
27
|
+
private val activeNotifications = mutableMapOf<String, Boolean>()
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Register a new notification instance.
|
|
31
|
+
*
|
|
32
|
+
* @param key Unique identifier for this notification
|
|
33
|
+
* @param notification The notification instance to register
|
|
34
|
+
*/
|
|
35
|
+
fun registerNotification(
|
|
36
|
+
key: String,
|
|
37
|
+
notification: BaseNotification,
|
|
38
|
+
) {
|
|
39
|
+
notifications[key] = notification
|
|
40
|
+
Log.d(TAG, "Registered notification: $key")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Initialize and show a notification.
|
|
45
|
+
*
|
|
46
|
+
* @param key The unique identifier of the notification
|
|
47
|
+
* @param options Configuration options from JavaScript
|
|
48
|
+
*/
|
|
49
|
+
fun showNotification(
|
|
50
|
+
key: String,
|
|
51
|
+
options: ReadableMap?,
|
|
52
|
+
) {
|
|
53
|
+
val notification = notifications[key]
|
|
54
|
+
if (notification == null) {
|
|
55
|
+
Log.w(TAG, "Notification not found: $key")
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
val builtNotification = notification.init(options)
|
|
61
|
+
displayNotification(notification.getNotificationId(), builtNotification)
|
|
62
|
+
activeNotifications[key] = true
|
|
63
|
+
|
|
64
|
+
// Subscribe to foreground service for persistent notifications
|
|
65
|
+
ForegroundServiceManager.subscribe("notification_$key")
|
|
66
|
+
|
|
67
|
+
Log.d(TAG, "Showing notification: $key")
|
|
68
|
+
} catch (e: Exception) {
|
|
69
|
+
Log.e(TAG, "Error showing notification $key: ${e.message}", e)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Update an existing notification with new options.
|
|
75
|
+
*
|
|
76
|
+
* @param key The unique identifier of the notification
|
|
77
|
+
* @param options New configuration options from JavaScript
|
|
78
|
+
*/
|
|
79
|
+
fun updateNotification(
|
|
80
|
+
key: String,
|
|
81
|
+
options: ReadableMap?,
|
|
82
|
+
) {
|
|
83
|
+
if (!activeNotifications.getOrDefault(key, false)) {
|
|
84
|
+
Log.w(TAG, "Cannot update inactive notification: $key")
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
val notification = notifications[key]
|
|
89
|
+
if (notification == null) {
|
|
90
|
+
Log.w(TAG, "Notification not found: $key")
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
val builtNotification = notification.update(options)
|
|
96
|
+
displayNotification(notification.getNotificationId(), builtNotification)
|
|
97
|
+
Log.d(TAG, "Updated notification: $key")
|
|
98
|
+
} catch (e: Exception) {
|
|
99
|
+
Log.e(TAG, "Error updating notification $key: ${e.message}", e)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Hide and reset a notification.
|
|
105
|
+
*
|
|
106
|
+
* @param key The unique identifier of the notification
|
|
107
|
+
*/
|
|
108
|
+
fun hideNotification(key: String) {
|
|
109
|
+
val notification = notifications[key]
|
|
110
|
+
if (notification == null) {
|
|
111
|
+
Log.w(TAG, "Notification not found: $key")
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
cancelNotification(notification.getNotificationId())
|
|
117
|
+
notification.reset()
|
|
118
|
+
activeNotifications[key] = false
|
|
119
|
+
|
|
120
|
+
// Unsubscribe from foreground service
|
|
121
|
+
ForegroundServiceManager.unsubscribe("notification_$key")
|
|
122
|
+
|
|
123
|
+
Log.d(TAG, "Hiding notification: $key")
|
|
124
|
+
} catch (e: Exception) {
|
|
125
|
+
Log.e(TAG, "Error hiding notification $key: ${e.message}", e)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Unregister and cleanup a notification.
|
|
131
|
+
*
|
|
132
|
+
* @param key The unique identifier of the notification
|
|
133
|
+
*/
|
|
134
|
+
fun unregisterNotification(key: String) {
|
|
135
|
+
hideNotification(key)
|
|
136
|
+
notifications.remove(key)
|
|
137
|
+
activeNotifications.remove(key)
|
|
138
|
+
Log.d(TAG, "Unregistered notification: $key")
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if a notification is currently active.
|
|
143
|
+
*/
|
|
144
|
+
fun isNotificationActive(key: String): Boolean = activeNotifications.getOrDefault(key, false)
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get all registered notification keys.
|
|
148
|
+
*/
|
|
149
|
+
fun getRegisteredKeys(): Set<String> = notifications.keys.toSet()
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Cleanup all notifications.
|
|
153
|
+
*/
|
|
154
|
+
fun cleanup() {
|
|
155
|
+
notifications.keys.toList().forEach { key ->
|
|
156
|
+
hideNotification(key)
|
|
157
|
+
}
|
|
158
|
+
notifications.clear()
|
|
159
|
+
activeNotifications.clear()
|
|
160
|
+
builtNotifications.clear()
|
|
161
|
+
|
|
162
|
+
// Cleanup foreground service manager
|
|
163
|
+
ForegroundServiceManager.cleanup()
|
|
164
|
+
|
|
165
|
+
Log.d(TAG, "Cleaned up all notifications")
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private fun displayNotification(
|
|
169
|
+
id: Int,
|
|
170
|
+
notification: Notification,
|
|
171
|
+
) {
|
|
172
|
+
val context = reactContext.get() ?: throw IllegalStateException("React context is null")
|
|
173
|
+
Log.d(TAG, "Displaying notification with ID: $id")
|
|
174
|
+
try {
|
|
175
|
+
// Store notification for foreground service access
|
|
176
|
+
builtNotifications[id] = notification
|
|
177
|
+
|
|
178
|
+
NotificationManagerCompat.from(context).notify(id, notification)
|
|
179
|
+
Log.d(TAG, "Notification posted successfully with ID: $id")
|
|
180
|
+
} catch (e: Exception) {
|
|
181
|
+
Log.e(TAG, "Error posting notification: ${e.message}", e)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private fun cancelNotification(id: Int) {
|
|
186
|
+
val context = reactContext.get() ?: return
|
|
187
|
+
NotificationManagerCompat.from(context).cancel(id)
|
|
188
|
+
// Clean up stored notification
|
|
189
|
+
builtNotifications.remove(id)
|
|
190
|
+
}
|
|
191
|
+
}
|