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
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
#include <audioapi/android/core/AndroidAudioRecorder.h>
|
|
3
3
|
#include <audioapi/android/core/utils/AndroidFileWriterBackend.h>
|
|
4
4
|
#include <audioapi/android/core/utils/AndroidRecorderCallback.h>
|
|
5
|
+
|
|
6
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
5
7
|
#include <audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h>
|
|
8
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
9
|
+
|
|
6
10
|
#include <audioapi/android/core/utils/miniaudioBackend/MiniAudioFileWriter.h>
|
|
7
11
|
#include <audioapi/core/sources/RecorderAdapterNode.h>
|
|
8
12
|
#include <audioapi/core/utils/Constants.h>
|
|
@@ -29,6 +33,25 @@ AndroidAudioRecorder::AndroidAudioRecorder(
|
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
AndroidAudioRecorder::~AndroidAudioRecorder() {
|
|
36
|
+
{
|
|
37
|
+
std::scoped_lock dtorLock(callbackMutex_, fileWriterMutex_, adapterNodeMutex_);
|
|
38
|
+
|
|
39
|
+
if (usesFileOutput()) {
|
|
40
|
+
fileOutputEnabled_.store(false, std::memory_order_release);
|
|
41
|
+
fileWriter_->closeFile();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (usesCallback()) {
|
|
45
|
+
callbackOutputEnabled_.store(false, std::memory_order_release);
|
|
46
|
+
dataCallback_->cleanup();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (isConnected()) {
|
|
50
|
+
isConnected_.store(false, std::memory_order_release);
|
|
51
|
+
adapterNode_->cleanup();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
32
55
|
nativeAudioRecorder_.release();
|
|
33
56
|
|
|
34
57
|
if (mStream_) {
|
|
@@ -86,7 +109,7 @@ Result<std::string, std::string> AndroidAudioRecorder::start() {
|
|
|
86
109
|
|
|
87
110
|
if (usesFileOutput()) {
|
|
88
111
|
auto fileResult =
|
|
89
|
-
std::
|
|
112
|
+
std::static_pointer_cast<AndroidFileWriterBackend>(fileWriter_)
|
|
90
113
|
->openFile(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
91
114
|
|
|
92
115
|
if (!fileResult.is_ok()) {
|
|
@@ -98,7 +121,7 @@ Result<std::string, std::string> AndroidAudioRecorder::start() {
|
|
|
98
121
|
}
|
|
99
122
|
|
|
100
123
|
if (usesCallback()) {
|
|
101
|
-
std::
|
|
124
|
+
std::static_pointer_cast<AndroidRecorderCallback>(dataCallback_)
|
|
102
125
|
->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
103
126
|
}
|
|
104
127
|
|
|
@@ -172,13 +195,18 @@ Result<std::string, std::string> AndroidAudioRecorder::enableFileOutput(
|
|
|
172
195
|
if (properties->format == AudioFileProperties::Format::WAV) {
|
|
173
196
|
fileWriter_ = std::make_shared<MiniAudioFileWriter>(audioEventHandlerRegistry_, properties);
|
|
174
197
|
} else {
|
|
198
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
175
199
|
fileWriter_ = std::make_shared<android::ffmpeg::FFmpegAudioFileWriter>(
|
|
176
200
|
audioEventHandlerRegistry_, properties);
|
|
201
|
+
#else
|
|
202
|
+
return Result<std::string, std::string>::Err(
|
|
203
|
+
"FFmpeg backend is disabled. Cannot create file writer for the requested format. Use WAV format instead.");
|
|
204
|
+
#endif
|
|
177
205
|
}
|
|
178
206
|
|
|
179
207
|
if (!isIdle()) {
|
|
180
208
|
auto fileResult =
|
|
181
|
-
std::
|
|
209
|
+
std::static_pointer_cast<AndroidFileWriterBackend>(fileWriter_)
|
|
182
210
|
->openFile(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
183
211
|
|
|
184
212
|
if (!fileResult.is_ok()) {
|
|
@@ -227,7 +255,7 @@ Result<NoneType, std::string> AndroidAudioRecorder::setOnAudioReadyCallback(
|
|
|
227
255
|
audioEventHandlerRegistry_, sampleRate, bufferLength, channelCount, callbackId);
|
|
228
256
|
|
|
229
257
|
if (!isIdle()) {
|
|
230
|
-
std::
|
|
258
|
+
std::static_pointer_cast<AndroidRecorderCallback>(dataCallback_)
|
|
231
259
|
->prepare(streamSampleRate_, streamChannelCount_, streamMaxBufferSizeInFrames_);
|
|
232
260
|
}
|
|
233
261
|
|
|
@@ -271,14 +299,14 @@ oboe::DataCallbackResult AndroidAudioRecorder::onAudioReady(
|
|
|
271
299
|
|
|
272
300
|
if (usesFileOutput()) {
|
|
273
301
|
if (auto fileWriterLock = Locker::tryLock(fileWriterMutex_)) {
|
|
274
|
-
std::
|
|
302
|
+
std::static_pointer_cast<AndroidFileWriterBackend>(fileWriter_)
|
|
275
303
|
->writeAudioData(audioData, numFrames);
|
|
276
304
|
}
|
|
277
305
|
}
|
|
278
306
|
|
|
279
307
|
if (usesCallback()) {
|
|
280
308
|
if (auto callbackLock = Locker::tryLock(callbackMutex_)) {
|
|
281
|
-
std::
|
|
309
|
+
std::static_pointer_cast<AndroidRecorderCallback>(dataCallback_)
|
|
282
310
|
->receiveAudioData(audioData, numFrames);
|
|
283
311
|
}
|
|
284
312
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
2
|
+
|
|
1
3
|
extern "C" {
|
|
2
4
|
#include <libavcodec/avcodec.h>
|
|
3
5
|
#include <libavformat/avformat.h>
|
|
@@ -508,3 +510,5 @@ CloseFileResult FFmpegAudioFileWriter::finalizeOutput() {
|
|
|
508
510
|
}
|
|
509
511
|
|
|
510
512
|
} // namespace audioapi::android::ffmpeg
|
|
513
|
+
|
|
514
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
#if !RN_AUDIO_API_FFMPEG_DISABLED
|
|
2
|
+
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
1
5
|
extern "C" {
|
|
2
6
|
#include <libavcodec/avcodec.h>
|
|
3
7
|
#include <libavformat/avformat.h>
|
|
@@ -46,3 +50,7 @@ template<> inline void AvDtor<AVAudioFifo>::operator()(AVAudioFifo* fifo) const
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
} // namespace audioapi::android::ffmpeg
|
|
53
|
+
|
|
54
|
+
#else
|
|
55
|
+
|
|
56
|
+
#endif // RN_AUDIO_API_FFMPEG_DISABLED
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.swmansion.audioapi
|
|
2
2
|
|
|
3
3
|
import com.facebook.jni.HybridData
|
|
4
|
+
import com.facebook.react.bridge.Arguments
|
|
4
5
|
import com.facebook.react.bridge.LifecycleEventListener
|
|
5
6
|
import com.facebook.react.bridge.Promise
|
|
6
7
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
@@ -9,6 +10,7 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
9
10
|
import com.facebook.react.common.annotations.FrameworkAPI
|
|
10
11
|
import com.facebook.react.module.annotations.ReactModule
|
|
11
12
|
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
|
|
13
|
+
import com.swmansion.audioapi.system.ForegroundServiceManager
|
|
12
14
|
import com.swmansion.audioapi.system.MediaSessionManager
|
|
13
15
|
import com.swmansion.audioapi.system.NativeFileInfo
|
|
14
16
|
import com.swmansion.audioapi.system.PermissionRequestListener
|
|
@@ -86,7 +88,8 @@ class AudioAPIModule(
|
|
|
86
88
|
|
|
87
89
|
override fun invalidate() {
|
|
88
90
|
reactContext.get()?.removeLifecycleEventListener(this)
|
|
89
|
-
//
|
|
91
|
+
// Cleanup foreground service manager
|
|
92
|
+
ForegroundServiceManager.cleanup()
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
override fun getDevicePreferredSampleRate(): Double = MediaSessionManager.getDevicePreferredSampleRate()
|
|
@@ -111,21 +114,6 @@ class AudioAPIModule(
|
|
|
111
114
|
// nothing to do here
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
override fun setLockScreenInfo(info: ReadableMap?) {
|
|
115
|
-
MediaSessionManager.setLockScreenInfo(info)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
override fun resetLockScreenInfo() {
|
|
119
|
-
MediaSessionManager.resetLockScreenInfo()
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
override fun enableRemoteCommand(
|
|
123
|
-
name: String?,
|
|
124
|
-
enabled: Boolean,
|
|
125
|
-
) {
|
|
126
|
-
MediaSessionManager.enableRemoteCommand(name!!, enabled)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
117
|
override fun observeAudioInterruptions(enabled: Boolean) {
|
|
130
118
|
MediaSessionManager.observeAudioInterruptions(enabled)
|
|
131
119
|
}
|
|
@@ -147,7 +135,167 @@ class AudioAPIModule(
|
|
|
147
135
|
promise.resolve(MediaSessionManager.checkRecordingPermissions())
|
|
148
136
|
}
|
|
149
137
|
|
|
138
|
+
override fun requestNotificationPermissions(promise: Promise) {
|
|
139
|
+
val permissionRequestListener = PermissionRequestListener(promise)
|
|
140
|
+
MediaSessionManager.requestNotificationPermissions(permissionRequestListener)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
override fun checkNotificationPermissions(promise: Promise) {
|
|
144
|
+
promise.resolve(MediaSessionManager.checkNotificationPermissions())
|
|
145
|
+
}
|
|
146
|
+
|
|
150
147
|
override fun getDevicesInfo(promise: Promise) {
|
|
151
148
|
promise.resolve(MediaSessionManager.getDevicesInfo())
|
|
152
149
|
}
|
|
150
|
+
|
|
151
|
+
// New notification system methods
|
|
152
|
+
override fun registerNotification(
|
|
153
|
+
type: String?,
|
|
154
|
+
key: String?,
|
|
155
|
+
promise: Promise?,
|
|
156
|
+
) {
|
|
157
|
+
try {
|
|
158
|
+
if (type == null || key == null) {
|
|
159
|
+
val result = Arguments.createMap()
|
|
160
|
+
result.putBoolean("success", false)
|
|
161
|
+
result.putString("error", "Type and key are required")
|
|
162
|
+
promise?.resolve(result)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
MediaSessionManager.registerNotification(type, key)
|
|
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)
|
|
221
|
+
|
|
222
|
+
val result = Arguments.createMap()
|
|
223
|
+
result.putBoolean("success", true)
|
|
224
|
+
promise?.resolve(result)
|
|
225
|
+
} catch (e: Exception) {
|
|
226
|
+
val result = Arguments.createMap()
|
|
227
|
+
result.putBoolean("success", false)
|
|
228
|
+
result.putString("error", e.message ?: "Unknown error")
|
|
229
|
+
promise?.resolve(result)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
override fun hideNotification(
|
|
234
|
+
key: String?,
|
|
235
|
+
promise: Promise?,
|
|
236
|
+
) {
|
|
237
|
+
try {
|
|
238
|
+
if (key == null) {
|
|
239
|
+
val result = Arguments.createMap()
|
|
240
|
+
result.putBoolean("success", false)
|
|
241
|
+
result.putString("error", "Key is required")
|
|
242
|
+
promise?.resolve(result)
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
MediaSessionManager.hideNotification(key)
|
|
247
|
+
|
|
248
|
+
val result = Arguments.createMap()
|
|
249
|
+
result.putBoolean("success", true)
|
|
250
|
+
promise?.resolve(result)
|
|
251
|
+
} catch (e: Exception) {
|
|
252
|
+
val result = Arguments.createMap()
|
|
253
|
+
result.putBoolean("success", false)
|
|
254
|
+
result.putString("error", e.message ?: "Unknown error")
|
|
255
|
+
promise?.resolve(result)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
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
|
+
override fun isNotificationActive(
|
|
286
|
+
key: String?,
|
|
287
|
+
promise: Promise?,
|
|
288
|
+
) {
|
|
289
|
+
try {
|
|
290
|
+
if (key == null) {
|
|
291
|
+
promise?.resolve(false)
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
val isActive = MediaSessionManager.isNotificationActive(key)
|
|
296
|
+
promise?.resolve(isActive)
|
|
297
|
+
} catch (e: Exception) {
|
|
298
|
+
promise?.resolve(false)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
153
301
|
}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
package com.swmansion.audioapi.core
|
|
2
2
|
|
|
3
3
|
import com.facebook.common.internal.DoNotStrip
|
|
4
|
-
import com.swmansion.audioapi.system.
|
|
4
|
+
import com.swmansion.audioapi.system.ForegroundServiceManager
|
|
5
|
+
import java.util.UUID
|
|
5
6
|
|
|
6
7
|
@DoNotStrip
|
|
7
8
|
class NativeAudioPlayer {
|
|
8
|
-
private var
|
|
9
|
+
private var playerId: String? = null
|
|
9
10
|
|
|
10
11
|
@DoNotStrip
|
|
11
12
|
fun start() {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
if (playerId == null) {
|
|
14
|
+
playerId = UUID.randomUUID().toString()
|
|
15
|
+
ForegroundServiceManager.subscribe("player_$playerId")
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
@DoNotStrip
|
|
17
20
|
fun stop() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
playerId?.let {
|
|
22
|
+
ForegroundServiceManager.unsubscribe("player_$it")
|
|
23
|
+
playerId = null
|
|
21
24
|
}
|
|
22
|
-
MediaSessionManager.stopForegroundServiceIfNecessary()
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
package com.swmansion.audioapi.core
|
|
2
2
|
|
|
3
3
|
import com.facebook.common.internal.DoNotStrip
|
|
4
|
-
import com.swmansion.audioapi.system.
|
|
4
|
+
import com.swmansion.audioapi.system.ForegroundServiceManager
|
|
5
|
+
import java.util.UUID
|
|
5
6
|
|
|
6
7
|
@DoNotStrip
|
|
7
8
|
class NativeAudioRecorder {
|
|
8
|
-
private var
|
|
9
|
+
private var recorderId: String? = null
|
|
9
10
|
|
|
10
11
|
@DoNotStrip
|
|
11
12
|
fun start() {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
if (recorderId == null) {
|
|
14
|
+
recorderId = UUID.randomUUID().toString()
|
|
15
|
+
ForegroundServiceManager.subscribe("recorder_$recorderId")
|
|
16
|
+
}
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
@DoNotStrip
|
|
17
20
|
fun stop() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
recorderId?.let {
|
|
22
|
+
ForegroundServiceManager.unsubscribe("recorder_$it")
|
|
23
|
+
recorderId = null
|
|
21
24
|
}
|
|
22
|
-
MediaSessionManager.stopForegroundServiceIfNecessary()
|
|
23
25
|
}
|
|
24
26
|
}
|
|
@@ -11,7 +11,6 @@ import java.util.HashMap
|
|
|
11
11
|
class AudioFocusListener(
|
|
12
12
|
private val audioManager: WeakReference<AudioManager>,
|
|
13
13
|
private val audioAPIModule: WeakReference<AudioAPIModule>,
|
|
14
|
-
private val lockScreenManager: WeakReference<LockScreenManager>,
|
|
15
14
|
) : AudioManager.OnAudioFocusChangeListener {
|
|
16
15
|
private var focusRequest: AudioFocusRequest? = null
|
|
17
16
|
|
|
@@ -22,7 +21,7 @@ class AudioFocusListener(
|
|
|
22
21
|
val body =
|
|
23
22
|
HashMap<String, Any>().apply {
|
|
24
23
|
put("type", "began")
|
|
25
|
-
put("
|
|
24
|
+
put("isTransient", false)
|
|
26
25
|
}
|
|
27
26
|
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
|
|
28
27
|
}
|
|
@@ -31,7 +30,7 @@ class AudioFocusListener(
|
|
|
31
30
|
val body =
|
|
32
31
|
HashMap<String, Any>().apply {
|
|
33
32
|
put("type", "began")
|
|
34
|
-
put("
|
|
33
|
+
put("isTransient", true)
|
|
35
34
|
}
|
|
36
35
|
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
|
|
37
36
|
}
|
|
@@ -40,7 +39,7 @@ class AudioFocusListener(
|
|
|
40
39
|
val body =
|
|
41
40
|
HashMap<String, Any>().apply {
|
|
42
41
|
put("type", "ended")
|
|
43
|
-
put("
|
|
42
|
+
put("isTransient", false)
|
|
44
43
|
}
|
|
45
44
|
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
|
|
46
45
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
package com.swmansion.audioapi.system
|
|
2
|
+
|
|
3
|
+
import android.app.Notification
|
|
4
|
+
import android.app.NotificationChannel
|
|
5
|
+
import android.app.NotificationManager
|
|
6
|
+
import android.app.Service
|
|
7
|
+
import android.content.Context
|
|
8
|
+
import android.content.Intent
|
|
9
|
+
import android.content.pm.ServiceInfo
|
|
10
|
+
import android.os.Build
|
|
11
|
+
import android.os.IBinder
|
|
12
|
+
import android.util.Log
|
|
13
|
+
import androidx.core.app.NotificationCompat
|
|
14
|
+
import com.swmansion.audioapi.system.MediaSessionManager.CHANNEL_ID
|
|
15
|
+
import com.swmansion.audioapi.system.notification.NotificationRegistry
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Centralized foreground service that can be used by any component that needs foreground capabilities.
|
|
19
|
+
*/
|
|
20
|
+
class CentralizedForegroundService : Service() {
|
|
21
|
+
companion object {
|
|
22
|
+
private const val TAG = "CentralizedForegroundService"
|
|
23
|
+
private const val NOTIFICATION_ID = 100
|
|
24
|
+
const val ACTION_START = "START_FOREGROUND"
|
|
25
|
+
const val ACTION_STOP = "STOP_FOREGROUND"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override fun onBind(intent: Intent?): IBinder? = null
|
|
29
|
+
|
|
30
|
+
override fun onStartCommand(
|
|
31
|
+
intent: Intent?,
|
|
32
|
+
flags: Int,
|
|
33
|
+
startId: Int,
|
|
34
|
+
): Int {
|
|
35
|
+
when (intent?.action) {
|
|
36
|
+
ACTION_START -> {
|
|
37
|
+
startForegroundWithNotification()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
ACTION_STOP -> {
|
|
41
|
+
stopForeground(STOP_FOREGROUND_REMOVE)
|
|
42
|
+
stopSelf()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return START_NOT_STICKY
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private fun startForegroundWithNotification() {
|
|
49
|
+
try {
|
|
50
|
+
createNotificationChannelIfNeeded()
|
|
51
|
+
|
|
52
|
+
// Try to use an existing notification first
|
|
53
|
+
val existingNotification = findExistingNotification()
|
|
54
|
+
val (notificationId, notification) =
|
|
55
|
+
if (existingNotification != null) {
|
|
56
|
+
existingNotification
|
|
57
|
+
} else {
|
|
58
|
+
// Fallback to default service notification
|
|
59
|
+
NOTIFICATION_ID to createServiceNotification()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
63
|
+
startForeground(
|
|
64
|
+
notificationId,
|
|
65
|
+
notification,
|
|
66
|
+
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
|
|
67
|
+
)
|
|
68
|
+
} else {
|
|
69
|
+
startForeground(notificationId, notification)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Log.d(TAG, "Centralized foreground service started with notification ID: $notificationId")
|
|
73
|
+
} catch (e: Exception) {
|
|
74
|
+
Log.e(TAG, "Error starting foreground service: ${e.message}", e)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private fun findExistingNotification(): Pair<Int, Notification>? {
|
|
79
|
+
// Check for recording notification first (priority)
|
|
80
|
+
NotificationRegistry.getBuiltNotification(101)?.let {
|
|
81
|
+
return 101 to it
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check for playback notification
|
|
85
|
+
NotificationRegistry.getBuiltNotification(100)?.let {
|
|
86
|
+
return 100 to it
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
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
|
+
private fun createNotificationChannelIfNeeded() {
|
|
105
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
106
|
+
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
107
|
+
|
|
108
|
+
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
|
|
109
|
+
val channel =
|
|
110
|
+
NotificationChannel(
|
|
111
|
+
CHANNEL_ID,
|
|
112
|
+
"Audio Service",
|
|
113
|
+
NotificationManager.IMPORTANCE_LOW,
|
|
114
|
+
).apply {
|
|
115
|
+
description = "Background audio processing"
|
|
116
|
+
setShowBadge(false)
|
|
117
|
+
lockscreenVisibility = NotificationCompat.VISIBILITY_PUBLIC
|
|
118
|
+
}
|
|
119
|
+
notificationManager.createNotificationChannel(channel)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
override fun onDestroy() {
|
|
125
|
+
Log.d(TAG, "Centralized foreground service destroyed")
|
|
126
|
+
super.onDestroy()
|
|
127
|
+
}
|
|
128
|
+
}
|