react-native-audio-api 0.11.0-nightly-b30bac9-20260114 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +3 -10
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +0 -4
- package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +4 -83
- package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +14 -29
- package/android/src/main/java/com/swmansion/audioapi/system/ForegroundServiceManager.kt +10 -9
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +10 -51
- package/android/src/main/java/com/swmansion/audioapi/system/notification/BaseNotification.kt +6 -14
- package/android/src/main/java/com/swmansion/audioapi/system/notification/NotificationRegistry.kt +79 -60
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +249 -411
- package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt +8 -3
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotification.kt +240 -222
- package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +11 -22
- package/android/src/main/java/com/swmansion/audioapi/system/notification/state/RecordingNotificationState.kt +24 -0
- package/android/src/main/res/layout/btn_round_ripple.xml +9 -0
- package/android/src/main/res/layout/notification_collapsed.xml +45 -0
- package/android/src/main/res/layout/notification_expanded.xml +44 -0
- package/android/src/oldarch/NativeAudioAPIModuleSpec.java +1 -13
- package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +1 -1
- package/ios/audioapi/ios/AudioAPIModule.mm +5 -48
- package/ios/audioapi/ios/system/notification/BaseNotification.h +0 -7
- package/ios/audioapi/ios/system/notification/NotificationRegistry.h +5 -25
- package/ios/audioapi/ios/system/notification/NotificationRegistry.mm +19 -64
- package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +4 -15
- package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +2 -1
- package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
- package/lib/commonjs/api.js +1 -29
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/core/AudioDecoder.js +42 -16
- package/lib/commonjs/core/AudioDecoder.js.map +1 -1
- package/lib/commonjs/core/AudioRecorder.js +2 -1
- package/lib/commonjs/core/AudioRecorder.js.map +1 -1
- package/lib/commonjs/core/AudioStretcher.js +2 -1
- package/lib/commonjs/core/AudioStretcher.js.map +1 -1
- package/lib/commonjs/core/BaseAudioContext.js +2 -5
- package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
- package/lib/commonjs/errors/AudioApiError.js +14 -0
- package/lib/commonjs/errors/AudioApiError.js.map +1 -0
- package/lib/commonjs/errors/index.js +7 -0
- package/lib/commonjs/errors/index.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/commonjs/specs/NativeAudioAPIModule.web.js +0 -9
- package/lib/commonjs/specs/NativeAudioAPIModule.web.js.map +1 -1
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js +40 -85
- package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/commonjs/system/notification/RecordingNotificationManager.ios.js +51 -0
- package/lib/commonjs/system/notification/RecordingNotificationManager.ios.js.map +1 -0
- package/lib/commonjs/system/notification/RecordingNotificationManager.js +30 -144
- package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/commonjs/system/notification/index.js +1 -9
- package/lib/commonjs/system/notification/index.js.map +1 -1
- package/lib/commonjs/utils/index.js +3 -2
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/commonjs/utils/paths.js +18 -0
- package/lib/commonjs/utils/paths.js.map +1 -0
- package/lib/commonjs/web-core/AudioContext.js +20 -11
- package/lib/commonjs/web-core/AudioContext.js.map +1 -1
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +0 -1
- package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +1 -6
- package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/module/AudioAPIModule/AudioAPIModule.js +2 -1
- package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
- package/lib/module/api.js +3 -2
- package/lib/module/api.js.map +1 -1
- package/lib/module/core/AudioDecoder.js +42 -16
- package/lib/module/core/AudioDecoder.js.map +1 -1
- package/lib/module/core/AudioRecorder.js +3 -1
- package/lib/module/core/AudioRecorder.js.map +1 -1
- package/lib/module/core/AudioStretcher.js +2 -1
- package/lib/module/core/AudioStretcher.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +2 -5
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/errors/AudioApiError.js +10 -0
- package/lib/module/errors/AudioApiError.js.map +1 -0
- package/lib/module/errors/index.js +1 -0
- package/lib/module/errors/index.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
- package/lib/module/specs/NativeAudioAPIModule.web.js +0 -9
- package/lib/module/specs/NativeAudioAPIModule.web.js.map +1 -1
- package/lib/module/system/notification/PlaybackNotificationManager.js +40 -85
- package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/module/system/notification/RecordingNotificationManager.ios.js +47 -0
- package/lib/module/system/notification/RecordingNotificationManager.ios.js.map +1 -0
- package/lib/module/system/notification/RecordingNotificationManager.js +30 -144
- package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/module/system/notification/index.js +1 -2
- package/lib/module/system/notification/index.js.map +1 -1
- package/lib/module/utils/index.js +3 -2
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/paths.js +12 -0
- package/lib/module/utils/paths.js.map +1 -0
- package/lib/module/web-core/AudioContext.js +20 -11
- package/lib/module/web-core/AudioContext.js.map +1 -1
- package/lib/module/web-system/notification/PlaybackNotificationManager.js +0 -1
- package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -1
- package/lib/module/web-system/notification/RecordingNotificationManager.js +1 -6
- package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -1
- package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/api.d.ts +3 -2
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/core/AudioDecoder.d.ts +2 -1
- package/lib/typescript/core/AudioDecoder.d.ts.map +1 -1
- package/lib/typescript/core/AudioRecorder.d.ts.map +1 -1
- package/lib/typescript/core/AudioStretcher.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +2 -2
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/errors/AudioApiError.d.ts +5 -0
- package/lib/typescript/errors/AudioApiError.d.ts.map +1 -0
- package/lib/typescript/errors/index.d.ts +1 -0
- package/lib/typescript/errors/index.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +1 -1
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts +2 -5
- package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
- package/lib/typescript/specs/NativeAudioAPIModule.web.d.ts +1 -4
- package/lib/typescript/specs/NativeAudioAPIModule.web.d.ts.map +1 -1
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +32 -9
- package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +26 -13
- package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -1
- package/lib/typescript/system/notification/RecordingNotificationManager.ios.d.ts +36 -0
- package/lib/typescript/system/notification/RecordingNotificationManager.ios.d.ts.map +1 -0
- package/lib/typescript/system/notification/index.d.ts +0 -1
- package/lib/typescript/system/notification/index.d.ts.map +1 -1
- package/lib/typescript/system/notification/types.d.ts +12 -22
- package/lib/typescript/system/notification/types.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +1 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/lib/typescript/utils/paths.d.ts +4 -0
- package/lib/typescript/utils/paths.d.ts.map +1 -0
- package/lib/typescript/web-core/AudioContext.d.ts +8 -9
- package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
- package/lib/typescript/web-core/BaseAudioContext.d.ts +6 -7
- package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +1 -2
- package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -1
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +2 -7
- package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/AudioAPIModule/AudioAPIModule.ts +2 -1
- package/src/api.ts +2 -8
- package/src/core/AudioDecoder.ts +91 -21
- package/src/core/AudioRecorder.ts +2 -1
- package/src/core/AudioStretcher.ts +2 -1
- package/src/core/BaseAudioContext.ts +4 -6
- package/src/errors/AudioApiError.ts +8 -0
- package/src/errors/index.ts +1 -0
- package/src/interfaces.ts +1 -1
- package/src/specs/NativeAudioAPIModule.ts +5 -15
- package/src/specs/NativeAudioAPIModule.web.ts +1 -12
- package/src/system/notification/PlaybackNotificationManager.ts +42 -117
- package/src/system/notification/RecordingNotificationManager.ios.ts +65 -0
- package/src/system/notification/RecordingNotificationManager.ts +33 -183
- package/src/system/notification/index.ts +0 -1
- package/src/system/notification/types.ts +15 -37
- package/src/types.ts +2 -0
- package/src/utils/index.ts +3 -2
- package/src/utils/paths.ts +11 -0
- package/src/web-core/AudioContext.tsx +34 -19
- package/src/web-core/BaseAudioContext.tsx +9 -7
- package/src/web-system/notification/PlaybackNotificationManager.ts +1 -7
- package/src/web-system/notification/RecordingNotificationManager.ts +1 -16
- package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioPlayer.kt +0 -26
- package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioRecorder.kt +0 -26
- package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt +0 -119
- package/lib/commonjs/system/notification/SimpleNotificationManager.js +0 -125
- package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +0 -1
- package/lib/module/system/notification/SimpleNotificationManager.js +0 -121
- package/lib/module/system/notification/SimpleNotificationManager.js.map +0 -1
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +0 -21
- package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +0 -1
- package/src/system/notification/SimpleNotificationManager.ts +0 -175
|
@@ -27,9 +27,7 @@ AndroidAudioRecorder::AndroidAudioRecorder(
|
|
|
27
27
|
: AudioRecorder(audioEventHandlerRegistry),
|
|
28
28
|
streamSampleRate_(0.0),
|
|
29
29
|
streamChannelCount_(0),
|
|
30
|
-
streamMaxBufferSizeInFrames_(0) {
|
|
31
|
-
nativeAudioRecorder_ = jni::make_global(NativeAudioRecorder::create());
|
|
32
|
-
}
|
|
30
|
+
streamMaxBufferSizeInFrames_(0) {}
|
|
33
31
|
|
|
34
32
|
/// @brief Destructor ensures that the audio stream and each output type are closed and flushed up remaining data.
|
|
35
33
|
/// TODO: Possibly locks here are not necessary, but we might have an issue with oboe having raw pointer to the
|
|
@@ -55,8 +53,6 @@ AndroidAudioRecorder::~AndroidAudioRecorder() {
|
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
nativeAudioRecorder_.release();
|
|
59
|
-
|
|
60
56
|
if (mStream_) {
|
|
61
57
|
mStream_->requestStop();
|
|
62
58
|
mStream_->close();
|
|
@@ -119,7 +115,7 @@ Result<std::string, std::string> AndroidAudioRecorder::start() {
|
|
|
119
115
|
return Result<std::string, std::string>::Err(streamResult.unwrap_err());
|
|
120
116
|
}
|
|
121
117
|
|
|
122
|
-
if (!mStream_
|
|
118
|
+
if (!mStream_) {
|
|
123
119
|
return Result<std::string, std::string>::Err("Audio stream is not initialized.");
|
|
124
120
|
}
|
|
125
121
|
|
|
@@ -153,8 +149,6 @@ Result<std::string, std::string> AndroidAudioRecorder::start() {
|
|
|
153
149
|
"Failed to start stream: " + std::string(oboe::convertToText(result)));
|
|
154
150
|
}
|
|
155
151
|
|
|
156
|
-
jni::ThreadScope::WithClassLoader([this]() { nativeAudioRecorder_->start(); });
|
|
157
|
-
|
|
158
152
|
state_.store(RecorderState::Recording, std::memory_order_release);
|
|
159
153
|
return Result<std::string, std::string>::Ok(std::format("file://{}", filePath_));
|
|
160
154
|
}
|
|
@@ -175,13 +169,12 @@ Result<std::tuple<std::string, double, double>, std::string> AndroidAudioRecorde
|
|
|
175
169
|
"Recorder is not in recording state.");
|
|
176
170
|
}
|
|
177
171
|
|
|
178
|
-
if (!mStream_
|
|
172
|
+
if (!mStream_) {
|
|
179
173
|
return Result<std::tuple<std::string, double, double>, std::string>::Err(
|
|
180
174
|
"Audio stream is not initialized.");
|
|
181
175
|
}
|
|
182
176
|
|
|
183
177
|
state_.store(RecorderState::Idle, std::memory_order_release);
|
|
184
|
-
jni::ThreadScope::WithClassLoader([this]() { nativeAudioRecorder_->stop(); });
|
|
185
178
|
mStream_->requestStop();
|
|
186
179
|
|
|
187
180
|
if (usesFileOutput()) {
|
|
@@ -17,8 +17,6 @@ AudioPlayer::AudioPlayer(
|
|
|
17
17
|
int channelCount)
|
|
18
18
|
: renderAudio_(renderAudio), sampleRate_(sampleRate), channelCount_(channelCount) {
|
|
19
19
|
isInitialized_ = openAudioStream();
|
|
20
|
-
|
|
21
|
-
nativeAudioPlayer_ = jni::make_global(NativeAudioPlayer::create());
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
bool AudioPlayer::openAudioStream() {
|
|
@@ -47,7 +45,6 @@ bool AudioPlayer::openAudioStream() {
|
|
|
47
45
|
|
|
48
46
|
bool AudioPlayer::start() {
|
|
49
47
|
if (mStream_) {
|
|
50
|
-
jni::ThreadScope::WithClassLoader([this]() { nativeAudioPlayer_->start(); });
|
|
51
48
|
auto result = mStream_->requestStart();
|
|
52
49
|
return result == oboe::Result::OK;
|
|
53
50
|
}
|
|
@@ -57,7 +54,6 @@ bool AudioPlayer::start() {
|
|
|
57
54
|
|
|
58
55
|
void AudioPlayer::stop() {
|
|
59
56
|
if (mStream_) {
|
|
60
|
-
jni::ThreadScope::WithClassLoader([this]() { nativeAudioPlayer_->stop(); });
|
|
61
57
|
mStream_->requestStop();
|
|
62
58
|
}
|
|
63
59
|
}
|
|
@@ -148,10 +148,11 @@ class AudioAPIModule(
|
|
|
148
148
|
promise.resolve(MediaSessionManager.getDevicesInfo())
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
//
|
|
152
|
-
override fun
|
|
151
|
+
// Notification system methods
|
|
152
|
+
override fun showNotification(
|
|
153
153
|
type: String?,
|
|
154
154
|
key: String?,
|
|
155
|
+
options: ReadableMap?,
|
|
155
156
|
promise: Promise?,
|
|
156
157
|
) {
|
|
157
158
|
try {
|
|
@@ -163,61 +164,7 @@ class AudioAPIModule(
|
|
|
163
164
|
return
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
MediaSessionManager.
|
|
167
|
-
|
|
168
|
-
val result = Arguments.createMap()
|
|
169
|
-
result.putBoolean("success", true)
|
|
170
|
-
promise?.resolve(result)
|
|
171
|
-
} catch (e: Exception) {
|
|
172
|
-
val result = Arguments.createMap()
|
|
173
|
-
result.putBoolean("success", false)
|
|
174
|
-
result.putString("error", e.message ?: "Unknown error")
|
|
175
|
-
promise?.resolve(result)
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
override fun showNotification(
|
|
180
|
-
key: String?,
|
|
181
|
-
options: ReadableMap?,
|
|
182
|
-
promise: Promise?,
|
|
183
|
-
) {
|
|
184
|
-
try {
|
|
185
|
-
if (key == null) {
|
|
186
|
-
val result = Arguments.createMap()
|
|
187
|
-
result.putBoolean("success", false)
|
|
188
|
-
result.putString("error", "Key is required")
|
|
189
|
-
promise?.resolve(result)
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
MediaSessionManager.showNotification(key, options)
|
|
194
|
-
|
|
195
|
-
val result = Arguments.createMap()
|
|
196
|
-
result.putBoolean("success", true)
|
|
197
|
-
promise?.resolve(result)
|
|
198
|
-
} catch (e: Exception) {
|
|
199
|
-
val result = Arguments.createMap()
|
|
200
|
-
result.putBoolean("success", false)
|
|
201
|
-
result.putString("error", e.message ?: "Unknown error")
|
|
202
|
-
promise?.resolve(result)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
override fun updateNotification(
|
|
207
|
-
key: String?,
|
|
208
|
-
options: ReadableMap?,
|
|
209
|
-
promise: Promise?,
|
|
210
|
-
) {
|
|
211
|
-
try {
|
|
212
|
-
if (key == null) {
|
|
213
|
-
val result = Arguments.createMap()
|
|
214
|
-
result.putBoolean("success", false)
|
|
215
|
-
result.putString("error", "Key is required")
|
|
216
|
-
promise?.resolve(result)
|
|
217
|
-
return
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
MediaSessionManager.updateNotification(key, options)
|
|
167
|
+
MediaSessionManager.showNotification(type, key, options)
|
|
221
168
|
|
|
222
169
|
val result = Arguments.createMap()
|
|
223
170
|
result.putBoolean("success", true)
|
|
@@ -256,32 +203,6 @@ class AudioAPIModule(
|
|
|
256
203
|
}
|
|
257
204
|
}
|
|
258
205
|
|
|
259
|
-
override fun unregisterNotification(
|
|
260
|
-
key: String?,
|
|
261
|
-
promise: Promise?,
|
|
262
|
-
) {
|
|
263
|
-
try {
|
|
264
|
-
if (key == null) {
|
|
265
|
-
val result = Arguments.createMap()
|
|
266
|
-
result.putBoolean("success", false)
|
|
267
|
-
result.putString("error", "Key is required")
|
|
268
|
-
promise?.resolve(result)
|
|
269
|
-
return
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
MediaSessionManager.unregisterNotification(key)
|
|
273
|
-
|
|
274
|
-
val result = Arguments.createMap()
|
|
275
|
-
result.putBoolean("success", true)
|
|
276
|
-
promise?.resolve(result)
|
|
277
|
-
} catch (e: Exception) {
|
|
278
|
-
val result = Arguments.createMap()
|
|
279
|
-
result.putBoolean("success", false)
|
|
280
|
-
result.putString("error", e.message ?: "Unknown error")
|
|
281
|
-
promise?.resolve(result)
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
206
|
override fun isNotificationActive(
|
|
286
207
|
key: String?,
|
|
287
208
|
promise: Promise?,
|
|
@@ -6,13 +6,14 @@ import android.app.NotificationManager
|
|
|
6
6
|
import android.app.Service
|
|
7
7
|
import android.content.Context
|
|
8
8
|
import android.content.Intent
|
|
9
|
-
import android.content.pm.ServiceInfo
|
|
10
9
|
import android.os.Build
|
|
11
10
|
import android.os.IBinder
|
|
12
11
|
import android.util.Log
|
|
13
12
|
import androidx.core.app.NotificationCompat
|
|
14
13
|
import com.swmansion.audioapi.system.MediaSessionManager.CHANNEL_ID
|
|
15
14
|
import com.swmansion.audioapi.system.notification.NotificationRegistry
|
|
15
|
+
import com.swmansion.audioapi.system.notification.PlaybackNotification
|
|
16
|
+
import com.swmansion.audioapi.system.notification.RecordingNotification
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Centralized foreground service that can be used by any component that needs foreground capabilities.
|
|
@@ -20,7 +21,6 @@ import com.swmansion.audioapi.system.notification.NotificationRegistry
|
|
|
20
21
|
class CentralizedForegroundService : Service() {
|
|
21
22
|
companion object {
|
|
22
23
|
private const val TAG = "CentralizedForegroundService"
|
|
23
|
-
private const val NOTIFICATION_ID = 100
|
|
24
24
|
const val ACTION_START = "START_FOREGROUND"
|
|
25
25
|
const val ACTION_STOP = "STOP_FOREGROUND"
|
|
26
26
|
}
|
|
@@ -49,21 +49,19 @@ class CentralizedForegroundService : Service() {
|
|
|
49
49
|
try {
|
|
50
50
|
createNotificationChannelIfNeeded()
|
|
51
51
|
|
|
52
|
-
//
|
|
52
|
+
// Get the first available notification
|
|
53
53
|
val existingNotification = findExistingNotification()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
54
|
+
if (existingNotification == null) {
|
|
55
|
+
Log.w(TAG, "No notification available to start foreground service")
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
val (notificationId, notification) = existingNotification
|
|
61
60
|
|
|
62
61
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
63
62
|
startForeground(
|
|
64
63
|
notificationId,
|
|
65
64
|
notification,
|
|
66
|
-
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
|
|
67
65
|
)
|
|
68
66
|
} else {
|
|
69
67
|
startForeground(notificationId, notification)
|
|
@@ -76,31 +74,18 @@ class CentralizedForegroundService : Service() {
|
|
|
76
74
|
}
|
|
77
75
|
|
|
78
76
|
private fun findExistingNotification(): Pair<Int, Notification>? {
|
|
79
|
-
// Check for
|
|
80
|
-
NotificationRegistry.getBuiltNotification(
|
|
81
|
-
return
|
|
77
|
+
// Check for playback notification first (priority)
|
|
78
|
+
NotificationRegistry.getBuiltNotification(PlaybackNotification.ID)?.let {
|
|
79
|
+
return PlaybackNotification.ID to it
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return 100 to it
|
|
82
|
+
NotificationRegistry.getBuiltNotification(RecordingNotification.ID)?.let {
|
|
83
|
+
return RecordingNotification.ID to it
|
|
87
84
|
}
|
|
88
85
|
|
|
89
86
|
return null
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
private fun createServiceNotification(): Notification =
|
|
93
|
-
NotificationCompat
|
|
94
|
-
.Builder(this, CHANNEL_ID)
|
|
95
|
-
.setContentTitle("Audio Service")
|
|
96
|
-
.setContentText("Audio processing in progress")
|
|
97
|
-
.setSmallIcon(android.R.drawable.ic_btn_speak_now)
|
|
98
|
-
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
99
|
-
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
100
|
-
.setOngoing(true)
|
|
101
|
-
.setAutoCancel(false)
|
|
102
|
-
.build()
|
|
103
|
-
|
|
104
89
|
private fun createNotificationChannelIfNeeded() {
|
|
105
90
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
106
91
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
@@ -4,6 +4,7 @@ import android.content.Intent
|
|
|
4
4
|
import android.os.Build
|
|
5
5
|
import android.util.Log
|
|
6
6
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.swmansion.audioapi.system.notification.BaseNotification
|
|
7
8
|
import java.lang.ref.WeakReference
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -14,7 +15,7 @@ object ForegroundServiceManager {
|
|
|
14
15
|
private const val TAG = "ForegroundServiceManager"
|
|
15
16
|
|
|
16
17
|
private lateinit var reactContext: WeakReference<ReactApplicationContext>
|
|
17
|
-
private val subscribers = mutableSetOf<
|
|
18
|
+
private val subscribers = mutableSetOf<BaseNotification>()
|
|
18
19
|
private var isServiceRunning = false
|
|
19
20
|
|
|
20
21
|
fun initialize(reactContext: WeakReference<ReactApplicationContext>) {
|
|
@@ -23,24 +24,24 @@ object ForegroundServiceManager {
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Subscribe to foreground service. Service will start if not already running.
|
|
26
|
-
* @param
|
|
27
|
+
* @param subscriber Unique identifier for the subscriber
|
|
27
28
|
*/
|
|
28
29
|
@Synchronized
|
|
29
|
-
fun subscribe(
|
|
30
|
-
if (subscribers.add(
|
|
31
|
-
Log.d(TAG, "Subscriber added: $
|
|
30
|
+
fun subscribe(subscriber: BaseNotification) {
|
|
31
|
+
if (subscribers.add(subscriber)) {
|
|
32
|
+
Log.d(TAG, "Subscriber added: $subscriber (total: ${subscribers.size})")
|
|
32
33
|
startServiceIfNeeded()
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Unsubscribe from foreground service. Service will stop if no more subscribers.
|
|
38
|
-
* @param
|
|
39
|
+
* @param subscriber Unique identifier for the subscriber
|
|
39
40
|
*/
|
|
40
41
|
@Synchronized
|
|
41
|
-
fun unsubscribe(
|
|
42
|
-
if (subscribers.remove(
|
|
43
|
-
Log.d(TAG, "Subscriber removed: $
|
|
42
|
+
fun unsubscribe(subscriber: BaseNotification) {
|
|
43
|
+
if (subscribers.remove(subscriber)) {
|
|
44
|
+
Log.d(TAG, "Subscriber removed: $subscriber (total: ${subscribers.size})")
|
|
44
45
|
stopServiceIfNotNeeded()
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -10,6 +10,7 @@ import android.media.AudioDeviceInfo
|
|
|
10
10
|
import android.media.AudioManager
|
|
11
11
|
import android.os.Build
|
|
12
12
|
import androidx.annotation.RequiresApi
|
|
13
|
+
import androidx.annotation.RequiresPermission
|
|
13
14
|
import androidx.core.app.ActivityCompat
|
|
14
15
|
import androidx.core.app.NotificationCompat
|
|
15
16
|
import androidx.core.content.ContextCompat
|
|
@@ -23,22 +24,17 @@ import com.swmansion.audioapi.system.PermissionRequestListener.Companion.RECORDI
|
|
|
23
24
|
import com.swmansion.audioapi.system.notification.NotificationRegistry
|
|
24
25
|
import com.swmansion.audioapi.system.notification.PlaybackNotification
|
|
25
26
|
import com.swmansion.audioapi.system.notification.PlaybackNotificationReceiver
|
|
26
|
-
import com.swmansion.audioapi.system.notification.RecordingNotification
|
|
27
|
-
import com.swmansion.audioapi.system.notification.RecordingNotificationReceiver
|
|
28
|
-
import com.swmansion.audioapi.system.notification.SimpleNotification
|
|
29
27
|
import java.lang.ref.WeakReference
|
|
30
28
|
|
|
31
29
|
object MediaSessionManager {
|
|
32
30
|
private lateinit var audioAPIModule: WeakReference<AudioAPIModule>
|
|
33
31
|
private lateinit var reactContext: WeakReference<ReactApplicationContext>
|
|
34
|
-
const val NOTIFICATION_ID = 100
|
|
35
32
|
const val CHANNEL_ID = "react-native-audio-api"
|
|
36
33
|
|
|
37
34
|
private lateinit var audioManager: AudioManager
|
|
38
35
|
private lateinit var audioFocusListener: AudioFocusListener
|
|
39
36
|
private lateinit var volumeChangeListener: VolumeChangeListener
|
|
40
37
|
private lateinit var playbackNotificationReceiver: PlaybackNotificationReceiver
|
|
41
|
-
private lateinit var recordingNotificationReceiver: RecordingNotificationReceiver
|
|
42
38
|
|
|
43
39
|
// New notification system
|
|
44
40
|
private lateinit var notificationRegistry: NotificationRegistry
|
|
@@ -64,6 +60,10 @@ object MediaSessionManager {
|
|
|
64
60
|
|
|
65
61
|
// Register PlaybackNotificationReceiver
|
|
66
62
|
val playbackFilter = IntentFilter(PlaybackNotificationReceiver.ACTION_NOTIFICATION_DISMISSED)
|
|
63
|
+
playbackFilter.addAction(PlaybackNotification.MEDIA_BUTTON)
|
|
64
|
+
playbackFilter.addAction(PlaybackNotificationReceiver.ACTION_SKIP_FORWARD)
|
|
65
|
+
playbackFilter.addAction(PlaybackNotificationReceiver.ACTION_SKIP_BACKWARD)
|
|
66
|
+
|
|
67
67
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
68
68
|
this.reactContext.get()!!.registerReceiver(playbackNotificationReceiver, playbackFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
69
69
|
} else {
|
|
@@ -75,29 +75,12 @@ object MediaSessionManager {
|
|
|
75
75
|
)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
// Set up RecordingNotificationReceiver
|
|
79
|
-
RecordingNotificationReceiver.setAudioAPIModule(audioAPIModule.get())
|
|
80
|
-
this.recordingNotificationReceiver = RecordingNotificationReceiver()
|
|
81
|
-
|
|
82
|
-
// Register RecordingNotificationReceiver
|
|
83
|
-
val recordingFilter = IntentFilter(RecordingNotificationReceiver.ACTION_NOTIFICATION_DISMISSED)
|
|
84
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
85
|
-
this.reactContext.get()!!.registerReceiver(recordingNotificationReceiver, recordingFilter, Context.RECEIVER_NOT_EXPORTED)
|
|
86
|
-
} else {
|
|
87
|
-
ContextCompat.registerReceiver(
|
|
88
|
-
this.reactContext.get()!!,
|
|
89
|
-
recordingNotificationReceiver,
|
|
90
|
-
recordingFilter,
|
|
91
|
-
ContextCompat.RECEIVER_NOT_EXPORTED,
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
78
|
this.audioFocusListener =
|
|
96
79
|
AudioFocusListener(WeakReference(this.audioManager), this.audioAPIModule)
|
|
97
80
|
this.volumeChangeListener = VolumeChangeListener(WeakReference(this.audioManager), this.audioAPIModule)
|
|
98
81
|
|
|
99
82
|
// Initialize new notification system
|
|
100
|
-
this.notificationRegistry = NotificationRegistry(this.reactContext)
|
|
83
|
+
this.notificationRegistry = NotificationRegistry(this.reactContext, this.audioAPIModule)
|
|
101
84
|
}
|
|
102
85
|
|
|
103
86
|
fun getDevicePreferredSampleRate(): Double {
|
|
@@ -263,43 +246,19 @@ object MediaSessionManager {
|
|
|
263
246
|
else -> "Other (${device.type})"
|
|
264
247
|
}
|
|
265
248
|
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
type: String,
|
|
269
|
-
key: String,
|
|
270
|
-
) {
|
|
271
|
-
val notification =
|
|
272
|
-
when (type) {
|
|
273
|
-
"simple" -> SimpleNotification(reactContext)
|
|
274
|
-
"playback" -> PlaybackNotification(reactContext, audioAPIModule, 100, "audio_playback")
|
|
275
|
-
"recording" -> RecordingNotification(reactContext, audioAPIModule, 101, "audio_recording23")
|
|
276
|
-
else -> throw IllegalArgumentException("Unknown notification type: $type")
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
notificationRegistry.registerNotification(key, notification)
|
|
280
|
-
}
|
|
281
|
-
|
|
249
|
+
// Notification system methods
|
|
250
|
+
@RequiresPermission(Manifest.permission.POST_NOTIFICATIONS)
|
|
282
251
|
fun showNotification(
|
|
252
|
+
type: String,
|
|
283
253
|
key: String,
|
|
284
254
|
options: ReadableMap?,
|
|
285
255
|
) {
|
|
286
|
-
notificationRegistry.showNotification(key, options)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
fun updateNotification(
|
|
290
|
-
key: String,
|
|
291
|
-
options: ReadableMap?,
|
|
292
|
-
) {
|
|
293
|
-
notificationRegistry.updateNotification(key, options)
|
|
256
|
+
notificationRegistry.showNotification(key, type, options)
|
|
294
257
|
}
|
|
295
258
|
|
|
296
259
|
fun hideNotification(key: String) {
|
|
297
260
|
notificationRegistry.hideNotification(key)
|
|
298
261
|
}
|
|
299
262
|
|
|
300
|
-
fun unregisterNotification(key: String) {
|
|
301
|
-
notificationRegistry.unregisterNotification(key)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
263
|
fun isNotificationActive(key: String): Boolean = notificationRegistry.isNotificationActive(key)
|
|
305
264
|
}
|
package/android/src/main/java/com/swmansion/audioapi/system/notification/BaseNotification.kt
CHANGED
|
@@ -10,28 +10,20 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
10
10
|
*/
|
|
11
11
|
interface BaseNotification {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
* This method should create the notification and prepare it for display.
|
|
13
|
+
* Show or update the notification with the provided options.
|
|
14
|
+
* This method should create/update the notification and prepare it for display.
|
|
15
|
+
* It handles both initial display and updates.
|
|
15
16
|
*
|
|
16
17
|
* @param options Configuration options from JavaScript side
|
|
17
18
|
* @return The built Notification ready to be shown
|
|
18
19
|
*/
|
|
19
|
-
fun
|
|
20
|
+
fun show(options: ReadableMap?): Notification
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
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.
|
|
23
|
+
* Hide the notification and cleanup resources.
|
|
32
24
|
* This should clear any stored data and stop any ongoing processes.
|
|
33
25
|
*/
|
|
34
|
-
fun
|
|
26
|
+
fun hide()
|
|
35
27
|
|
|
36
28
|
/**
|
|
37
29
|
* Get the unique ID for this notification.
|