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.
Files changed (134) hide show
  1. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +164 -16
  2. package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioPlayer.kt +10 -8
  3. package/android/src/main/java/com/swmansion/audioapi/core/NativeAudioRecorder.kt +10 -8
  4. package/android/src/main/java/com/swmansion/audioapi/system/AudioFocusListener.kt +8 -23
  5. package/android/src/main/java/com/swmansion/audioapi/system/CentralizedForegroundService.kt +127 -0
  6. package/android/src/main/java/com/swmansion/audioapi/system/ForegroundServiceManager.kt +116 -0
  7. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionManager.kt +115 -107
  8. package/android/src/main/java/com/swmansion/audioapi/system/PermissionRequestListener.kt +2 -1
  9. package/android/src/main/java/com/swmansion/audioapi/system/notification/BaseNotification.kt +47 -0
  10. package/android/src/main/java/com/swmansion/audioapi/system/notification/NotificationRegistry.kt +191 -0
  11. package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotification.kt +668 -0
  12. package/android/src/main/java/com/swmansion/audioapi/system/notification/PlaybackNotificationReceiver.kt +33 -0
  13. package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotification.kt +303 -0
  14. package/android/src/main/java/com/swmansion/audioapi/system/notification/RecordingNotificationReceiver.kt +43 -0
  15. package/android/src/main/java/com/swmansion/audioapi/system/notification/SimpleNotification.kt +119 -0
  16. package/ios/audioapi/ios/AudioAPIModule.h +2 -2
  17. package/ios/audioapi/ios/AudioAPIModule.mm +108 -18
  18. package/ios/audioapi/ios/system/AudioEngine.mm +2 -2
  19. package/ios/audioapi/ios/system/AudioSessionManager.mm +1 -1
  20. package/ios/audioapi/ios/system/NotificationManager.mm +1 -1
  21. package/ios/audioapi/ios/system/notification/BaseNotification.h +58 -0
  22. package/ios/audioapi/ios/system/notification/NotificationRegistry.h +70 -0
  23. package/ios/audioapi/ios/system/notification/NotificationRegistry.mm +172 -0
  24. package/ios/audioapi/ios/system/notification/PlaybackNotification.h +27 -0
  25. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +427 -0
  26. package/lib/commonjs/api.js +59 -10
  27. package/lib/commonjs/api.js.map +1 -1
  28. package/lib/commonjs/api.web.js +27 -14
  29. package/lib/commonjs/api.web.js.map +1 -1
  30. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
  31. package/lib/commonjs/system/AudioManager.js +6 -9
  32. package/lib/commonjs/system/AudioManager.js.map +1 -1
  33. package/lib/commonjs/system/index.js +13 -0
  34. package/lib/commonjs/system/index.js.map +1 -1
  35. package/lib/commonjs/system/notification/PlaybackNotificationManager.js +135 -0
  36. package/lib/commonjs/system/notification/PlaybackNotificationManager.js.map +1 -0
  37. package/lib/commonjs/system/notification/RecordingNotificationManager.js +182 -0
  38. package/lib/commonjs/system/notification/RecordingNotificationManager.js.map +1 -0
  39. package/lib/commonjs/system/notification/SimpleNotificationManager.js +122 -0
  40. package/lib/commonjs/system/notification/SimpleNotificationManager.js.map +1 -0
  41. package/lib/commonjs/system/notification/index.js +45 -0
  42. package/lib/commonjs/system/notification/index.js.map +1 -0
  43. package/lib/commonjs/system/notification/types.js +6 -0
  44. package/lib/commonjs/system/notification/types.js.map +1 -0
  45. package/lib/commonjs/web-system/index.js +17 -0
  46. package/lib/commonjs/web-system/index.js.map +1 -0
  47. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js +34 -0
  48. package/lib/commonjs/web-system/notification/PlaybackNotificationManager.js.map +1 -0
  49. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js +34 -0
  50. package/lib/commonjs/web-system/notification/RecordingNotificationManager.js.map +1 -0
  51. package/lib/commonjs/web-system/notification/index.js +21 -0
  52. package/lib/commonjs/web-system/notification/index.js.map +1 -0
  53. package/lib/module/api.js +5 -1
  54. package/lib/module/api.js.map +1 -1
  55. package/lib/module/api.web.js +3 -1
  56. package/lib/module/api.web.js.map +1 -1
  57. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
  58. package/lib/module/system/AudioManager.js +6 -9
  59. package/lib/module/system/AudioManager.js.map +1 -1
  60. package/lib/module/system/index.js +1 -0
  61. package/lib/module/system/index.js.map +1 -1
  62. package/lib/module/system/notification/PlaybackNotificationManager.js +131 -0
  63. package/lib/module/system/notification/PlaybackNotificationManager.js.map +1 -0
  64. package/lib/module/system/notification/RecordingNotificationManager.js +178 -0
  65. package/lib/module/system/notification/RecordingNotificationManager.js.map +1 -0
  66. package/lib/module/system/notification/SimpleNotificationManager.js +118 -0
  67. package/lib/module/system/notification/SimpleNotificationManager.js.map +1 -0
  68. package/lib/module/system/notification/index.js +7 -0
  69. package/lib/module/system/notification/index.js.map +1 -0
  70. package/lib/module/system/notification/types.js +4 -0
  71. package/lib/module/system/notification/types.js.map +1 -0
  72. package/lib/module/web-system/index.js +4 -0
  73. package/lib/module/web-system/index.js.map +1 -0
  74. package/lib/module/web-system/notification/PlaybackNotificationManager.js +30 -0
  75. package/lib/module/web-system/notification/PlaybackNotificationManager.js.map +1 -0
  76. package/lib/module/web-system/notification/RecordingNotificationManager.js +30 -0
  77. package/lib/module/web-system/notification/RecordingNotificationManager.js.map +1 -0
  78. package/lib/module/web-system/notification/index.js +5 -0
  79. package/lib/module/web-system/notification/index.js.map +1 -0
  80. package/lib/typescript/api.d.ts +3 -1
  81. package/lib/typescript/api.d.ts.map +1 -1
  82. package/lib/typescript/api.web.d.ts +3 -1
  83. package/lib/typescript/api.web.d.ts.map +1 -1
  84. package/lib/typescript/events/types.d.ts +4 -18
  85. package/lib/typescript/events/types.d.ts.map +1 -1
  86. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +16 -5
  87. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
  88. package/lib/typescript/system/AudioManager.d.ts +4 -5
  89. package/lib/typescript/system/AudioManager.d.ts.map +1 -1
  90. package/lib/typescript/system/index.d.ts +1 -0
  91. package/lib/typescript/system/index.d.ts.map +1 -1
  92. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts +22 -0
  93. package/lib/typescript/system/notification/PlaybackNotificationManager.d.ts.map +1 -0
  94. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts +23 -0
  95. package/lib/typescript/system/notification/RecordingNotificationManager.d.ts.map +1 -0
  96. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts +20 -0
  97. package/lib/typescript/system/notification/SimpleNotificationManager.d.ts.map +1 -0
  98. package/lib/typescript/system/notification/index.d.ts +5 -0
  99. package/lib/typescript/system/notification/index.d.ts.map +1 -0
  100. package/lib/typescript/system/notification/types.d.ts +65 -0
  101. package/lib/typescript/system/notification/types.d.ts.map +1 -0
  102. package/lib/typescript/system/types.d.ts +0 -16
  103. package/lib/typescript/system/types.d.ts.map +1 -1
  104. package/lib/typescript/web-system/index.d.ts +2 -0
  105. package/lib/typescript/web-system/index.d.ts.map +1 -0
  106. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts +19 -0
  107. package/lib/typescript/web-system/notification/PlaybackNotificationManager.d.ts.map +1 -0
  108. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts +19 -0
  109. package/lib/typescript/web-system/notification/RecordingNotificationManager.d.ts.map +1 -0
  110. package/lib/typescript/web-system/notification/index.d.ts +3 -0
  111. package/lib/typescript/web-system/notification/index.d.ts.map +1 -0
  112. package/package.json +1 -1
  113. package/src/api.ts +17 -2
  114. package/src/api.web.ts +7 -2
  115. package/src/events/types.ts +4 -20
  116. package/src/specs/NativeAudioAPIModule.ts +23 -7
  117. package/src/system/AudioManager.ts +10 -23
  118. package/src/system/index.ts +1 -0
  119. package/src/system/notification/PlaybackNotificationManager.ts +193 -0
  120. package/src/system/notification/RecordingNotificationManager.ts +242 -0
  121. package/src/system/notification/SimpleNotificationManager.ts +170 -0
  122. package/src/system/notification/index.ts +4 -0
  123. package/src/system/notification/types.ts +110 -0
  124. package/src/system/types.ts +0 -18
  125. package/src/web-system/index.ts +1 -0
  126. package/src/web-system/notification/PlaybackNotificationManager.ts +60 -0
  127. package/src/web-system/notification/RecordingNotificationManager.ts +60 -0
  128. package/src/web-system/notification/index.ts +2 -0
  129. package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +0 -347
  130. package/android/src/main/java/com/swmansion/audioapi/system/MediaNotificationManager.kt +0 -273
  131. package/android/src/main/java/com/swmansion/audioapi/system/MediaReceiver.kt +0 -57
  132. package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +0 -61
  133. package/ios/audioapi/ios/system/LockScreenManager.h +0 -23
  134. package/ios/audioapi/ios/system/LockScreenManager.mm +0 -314
package/src/api.ts CHANGED
@@ -28,6 +28,12 @@ export { default as WorkletSourceNode } from './core/WorkletSourceNode';
28
28
  export { default as useSystemVolume } from './hooks/useSystemVolume';
29
29
  export { default as AudioManager } from './system';
30
30
 
31
+ // Notification System
32
+ export {
33
+ PlaybackNotificationManager,
34
+ RecordingNotificationManager,
35
+ } from './system/notification';
36
+
31
37
  export {
32
38
  AudioWorkletRuntime,
33
39
  BiquadFilterType,
@@ -43,12 +49,21 @@ export {
43
49
  IOSCategory,
44
50
  IOSMode,
45
51
  IOSOption,
46
- LockScreenInfo,
47
- MediaState,
48
52
  PermissionStatus,
49
53
  SessionOptions,
50
54
  } from './system/types';
51
55
 
56
+ export {
57
+ NotificationManager,
58
+ PlaybackNotificationInfo,
59
+ PlaybackControlName,
60
+ PlaybackNotificationEventName,
61
+ RecordingNotificationInfo,
62
+ RecordingControlName,
63
+ RecordingNotificationEventName,
64
+ SimpleNotificationOptions,
65
+ } from './system/notification';
66
+
52
67
  export {
53
68
  IndexSizeError,
54
69
  InvalidAccessError,
package/src/api.web.ts CHANGED
@@ -32,11 +32,16 @@ export {
32
32
  IOSMode,
33
33
  IOSOption,
34
34
  SessionOptions,
35
- MediaState,
36
- LockScreenInfo,
37
35
  PermissionStatus,
38
36
  } from './system/types';
39
37
 
38
+ export {
39
+ PlaybackNotificationManager,
40
+ RecordingNotificationManager,
41
+ } from './web-system';
42
+
43
+ export * from './system/notification/types';
44
+
40
45
  export {
41
46
  IndexSizeError,
42
47
  InvalidAccessError,
@@ -1,4 +1,5 @@
1
1
  import AudioBuffer from '../core/AudioBuffer';
2
+ import { NotificationEvents } from '../system';
2
3
 
3
4
  export interface EventEmptyType {}
4
5
 
@@ -8,7 +9,7 @@ export interface EventTypeWithValue {
8
9
 
9
10
  interface OnInterruptionEventType {
10
11
  type: 'ended' | 'began';
11
- shouldResume: boolean;
12
+ isTransient: boolean;
12
13
  }
13
14
 
14
15
  interface OnRouteChangeEventType {
@@ -23,22 +24,7 @@ interface OnRouteChangeEventType {
23
24
  | 'NoSuitableRouteForCategory';
24
25
  }
25
26
 
26
- interface RemoteCommandEvents {
27
- remotePlay: EventEmptyType;
28
- remotePause: EventEmptyType;
29
- remoteStop: EventEmptyType;
30
- remoteTogglePlayPause: EventEmptyType;
31
- remoteChangePlaybackRate: EventTypeWithValue;
32
- remoteNextTrack: EventEmptyType;
33
- remotePreviousTrack: EventEmptyType;
34
- remoteSkipForward: EventTypeWithValue;
35
- remoteSkipBackward: EventTypeWithValue;
36
- remoteSeekForward: EventEmptyType;
37
- remoteSeekBackward: EventEmptyType;
38
- remoteChangePlaybackPosition: EventTypeWithValue;
39
- }
40
-
41
- type SystemEvents = RemoteCommandEvents & {
27
+ type SystemEvents = {
42
28
  volumeChange: EventTypeWithValue;
43
29
  interruption: OnInterruptionEventType;
44
30
  routeChange: OnRouteChangeEventType;
@@ -64,9 +50,7 @@ interface AudioAPIEvents {
64
50
  systemStateChanged: EventEmptyType; // to change
65
51
  }
66
52
 
67
- type AudioEvents = SystemEvents & AudioAPIEvents;
68
-
69
- export type RemoteCommandEventName = keyof RemoteCommandEvents;
53
+ type AudioEvents = SystemEvents & AudioAPIEvents & NotificationEvents;
70
54
 
71
55
  export type SystemEventName = keyof SystemEvents;
72
56
  export type SystemEventCallback<Name extends SystemEventName> = (
@@ -3,6 +3,10 @@ import { TurboModuleRegistry } from 'react-native';
3
3
  import type { TurboModule } from 'react-native';
4
4
  import { PermissionStatus, AudioDevicesInfo } from '../system/types';
5
5
 
6
+ type OptionsMap = { [key: string]: string | boolean | number | undefined };
7
+ type NotificationOpResponse = { success: boolean; error?: string };
8
+ type NotificationType = 'playback' | 'recording' | 'simple';
9
+
6
10
  interface Spec extends TurboModule {
7
11
  install(): boolean;
8
12
  getDevicePreferredSampleRate(): number;
@@ -17,14 +21,7 @@ interface Spec extends TurboModule {
17
21
  ): void;
18
22
  disableSessionManagement(): void;
19
23
 
20
- // Lock Screen Info
21
- setLockScreenInfo(info: {
22
- [key: string]: string | boolean | number | undefined;
23
- }): void;
24
- resetLockScreenInfo(): void;
25
-
26
24
  // Remote commands, system events and interruptions
27
- enableRemoteCommand(name: string, enabled: boolean): void;
28
25
  observeAudioInterruptions(enabled: boolean): void;
29
26
  activelyReclaimSession(enabled: boolean): void;
30
27
  observeVolumeChanges(enabled: boolean): void;
@@ -32,9 +29,28 @@ interface Spec extends TurboModule {
32
29
  // Permissions
33
30
  requestRecordingPermissions(): Promise<PermissionStatus>;
34
31
  checkRecordingPermissions(): Promise<PermissionStatus>;
32
+ requestNotificationPermissions(): Promise<PermissionStatus>;
33
+ checkNotificationPermissions(): Promise<PermissionStatus>;
35
34
 
36
35
  // Audio devices
37
36
  getDevicesInfo(): Promise<AudioDevicesInfo>;
37
+
38
+ // New notification system
39
+ registerNotification(
40
+ type: NotificationType,
41
+ key: string
42
+ ): Promise<NotificationOpResponse>;
43
+ showNotification(
44
+ key: string,
45
+ options: OptionsMap
46
+ ): Promise<NotificationOpResponse>;
47
+ updateNotification(
48
+ key: string,
49
+ options: OptionsMap
50
+ ): Promise<NotificationOpResponse>;
51
+ hideNotification(key: string): Promise<NotificationOpResponse>;
52
+ unregisterNotification(key: string): Promise<NotificationOpResponse>;
53
+ isNotificationActive(key: string): Promise<boolean>;
38
54
  }
39
55
 
40
56
  const NativeAudioAPIModule = TurboModuleRegistry.get<Spec>('AudioAPIModule');
@@ -1,16 +1,7 @@
1
+ import { SessionOptions, PermissionStatus, AudioDevicesInfo } from './types';
2
+ import { SystemEventName, SystemEventCallback } from '../events/types';
1
3
  import { AudioEventEmitter, AudioEventSubscription } from '../events';
2
- import {
3
- RemoteCommandEventName,
4
- SystemEventCallback,
5
- SystemEventName,
6
- } from '../events/types';
7
4
  import { NativeAudioAPIModule } from '../specs';
8
- import {
9
- AudioDevicesInfo,
10
- LockScreenInfo,
11
- PermissionStatus,
12
- SessionOptions,
13
- } from './types';
14
5
 
15
6
  class AudioManager {
16
7
  private readonly audioEventEmitter: AudioEventEmitter;
@@ -39,14 +30,6 @@ class AudioManager {
39
30
  NativeAudioAPIModule!.disableSessionManagement();
40
31
  }
41
32
 
42
- setLockScreenInfo(info: LockScreenInfo) {
43
- NativeAudioAPIModule!.setLockScreenInfo(info);
44
- }
45
-
46
- resetLockScreenInfo() {
47
- NativeAudioAPIModule!.resetLockScreenInfo();
48
- }
49
-
50
33
  observeAudioInterruptions(enabled: boolean) {
51
34
  NativeAudioAPIModule!.observeAudioInterruptions(enabled);
52
35
  }
@@ -72,10 +55,6 @@ class AudioManager {
72
55
  NativeAudioAPIModule!.observeVolumeChanges(enabled);
73
56
  }
74
57
 
75
- enableRemoteCommand(name: RemoteCommandEventName, enabled: boolean) {
76
- NativeAudioAPIModule!.enableRemoteCommand(name, enabled);
77
- }
78
-
79
58
  addSystemEventListener<Name extends SystemEventName>(
80
59
  name: Name,
81
60
  callback: SystemEventCallback<Name>
@@ -91,6 +70,14 @@ class AudioManager {
91
70
  return NativeAudioAPIModule!.checkRecordingPermissions();
92
71
  }
93
72
 
73
+ async requestNotificationPermissions(): Promise<PermissionStatus> {
74
+ return NativeAudioAPIModule!.requestNotificationPermissions();
75
+ }
76
+
77
+ async checkNotificationPermissions(): Promise<PermissionStatus> {
78
+ return NativeAudioAPIModule!.checkNotificationPermissions();
79
+ }
80
+
94
81
  async getDevicesInfo(): Promise<AudioDevicesInfo> {
95
82
  return NativeAudioAPIModule!.getDevicesInfo();
96
83
  }
@@ -1 +1,2 @@
1
1
  export { default } from './AudioManager';
2
+ export * from './notification';
@@ -0,0 +1,193 @@
1
+ import { NativeAudioAPIModule } from '../../specs';
2
+ import { AudioEventEmitter, AudioEventSubscription } from '../../events';
3
+ import type {
4
+ NotificationManager,
5
+ PlaybackNotificationInfo,
6
+ PlaybackControlName,
7
+ PlaybackNotificationEventName,
8
+ NotificationEvents,
9
+ } from './types';
10
+
11
+ /// Manager for media playback notifications with controls and MediaSession integration.
12
+ class PlaybackNotificationManager
13
+ implements
14
+ NotificationManager<
15
+ PlaybackNotificationInfo,
16
+ PlaybackNotificationInfo,
17
+ PlaybackNotificationEventName
18
+ >
19
+ {
20
+ private notificationKey = 'playback';
21
+ private isRegistered = false;
22
+ private isShown = false;
23
+ private audioEventEmitter: AudioEventEmitter;
24
+
25
+ constructor() {
26
+ this.audioEventEmitter = new AudioEventEmitter(global.AudioEventEmitter);
27
+ }
28
+
29
+ /// Register the playback notification (must be called before showing).
30
+ async register(): Promise<void> {
31
+ if (this.isRegistered) {
32
+ console.warn('PlaybackNotification is already registered');
33
+ return;
34
+ }
35
+
36
+ if (!NativeAudioAPIModule) {
37
+ throw new Error('NativeAudioAPIModule is not available');
38
+ }
39
+
40
+ const result = await NativeAudioAPIModule.registerNotification(
41
+ 'playback',
42
+ this.notificationKey
43
+ );
44
+
45
+ if (result.error) {
46
+ throw new Error(result.error);
47
+ }
48
+
49
+ this.isRegistered = true;
50
+ }
51
+
52
+ /// Show the notification with initial metadata.
53
+ async show(info: PlaybackNotificationInfo): Promise<void> {
54
+ if (!this.isRegistered) {
55
+ throw new Error(
56
+ 'PlaybackNotification must be registered before showing. Call register() first.'
57
+ );
58
+ }
59
+
60
+ if (!NativeAudioAPIModule) {
61
+ throw new Error('NativeAudioAPIModule is not available');
62
+ }
63
+
64
+ const result = await NativeAudioAPIModule.showNotification(
65
+ this.notificationKey,
66
+ info as Record<string, string | number | boolean | undefined>
67
+ );
68
+
69
+ if (result.error) {
70
+ throw new Error(result.error);
71
+ }
72
+
73
+ this.isShown = true;
74
+ }
75
+
76
+ /// Update the notification with new metadata or state.
77
+ async update(info: PlaybackNotificationInfo): Promise<void> {
78
+ if (!this.isShown) {
79
+ console.warn('PlaybackNotification is not shown. Call show() first.');
80
+ return;
81
+ }
82
+
83
+ if (!NativeAudioAPIModule) {
84
+ throw new Error('NativeAudioAPIModule is not available');
85
+ }
86
+
87
+ const result = await NativeAudioAPIModule.updateNotification(
88
+ this.notificationKey,
89
+ info as Record<string, string | number | boolean | undefined>
90
+ );
91
+
92
+ if (result.error) {
93
+ throw new Error(result.error);
94
+ }
95
+ }
96
+
97
+ /// Hide the notification (can be shown again later).
98
+ async hide(): Promise<void> {
99
+ if (!this.isShown) {
100
+ return;
101
+ }
102
+
103
+ if (!NativeAudioAPIModule) {
104
+ throw new Error('NativeAudioAPIModule is not available');
105
+ }
106
+
107
+ const result = await NativeAudioAPIModule.hideNotification(
108
+ this.notificationKey
109
+ );
110
+
111
+ if (result.error) {
112
+ throw new Error(result.error);
113
+ }
114
+
115
+ this.isShown = false;
116
+ }
117
+
118
+ /// Unregister the notification (must register again to use).
119
+ async unregister(): Promise<void> {
120
+ if (!this.isRegistered) {
121
+ return;
122
+ }
123
+
124
+ if (this.isShown) {
125
+ await this.hide();
126
+ }
127
+
128
+ if (!NativeAudioAPIModule) {
129
+ throw new Error('NativeAudioAPIModule is not available');
130
+ }
131
+
132
+ const result = await NativeAudioAPIModule.unregisterNotification(
133
+ this.notificationKey
134
+ );
135
+
136
+ if (result.error) {
137
+ throw new Error(result.error);
138
+ }
139
+
140
+ this.isRegistered = false;
141
+ }
142
+
143
+ /// Enable or disable a specific playback control.
144
+ async enableControl(
145
+ control: PlaybackControlName,
146
+ enabled: boolean
147
+ ): Promise<void> {
148
+ if (!this.isRegistered) {
149
+ console.warn('PlaybackNotification is not registered');
150
+ return;
151
+ }
152
+
153
+ if (!NativeAudioAPIModule) {
154
+ throw new Error('NativeAudioAPIModule is not available');
155
+ }
156
+
157
+ const params = { control, enabled };
158
+ const result = await NativeAudioAPIModule.updateNotification(
159
+ this.notificationKey,
160
+ params as Record<string, string | number | boolean | undefined>
161
+ );
162
+
163
+ if (result.error) {
164
+ throw new Error(result.error);
165
+ }
166
+ }
167
+
168
+ /// Check if the notification is currently active.
169
+ async isActive(): Promise<boolean> {
170
+ if (!NativeAudioAPIModule) {
171
+ return false;
172
+ }
173
+
174
+ return await NativeAudioAPIModule.isNotificationActive(
175
+ this.notificationKey
176
+ );
177
+ }
178
+
179
+ /// Add an event listener for notification actions.
180
+ addEventListener<T extends PlaybackNotificationEventName>(
181
+ eventName: T,
182
+ callback: (event: NotificationEvents[T]) => void
183
+ ): AudioEventSubscription {
184
+ return this.audioEventEmitter.addAudioEventListener(eventName, callback);
185
+ }
186
+
187
+ /** Remove an event listener. */
188
+ removeEventListener(subscription: AudioEventSubscription): void {
189
+ subscription.remove();
190
+ }
191
+ }
192
+
193
+ export default new PlaybackNotificationManager();
@@ -0,0 +1,242 @@
1
+ import { Platform } from 'react-native';
2
+ import { NativeAudioAPIModule } from '../../specs';
3
+ import { AudioEventEmitter, AudioEventSubscription } from '../../events';
4
+ import type {
5
+ NotificationManager,
6
+ RecordingNotificationInfo,
7
+ RecordingControlName,
8
+ RecordingNotificationEventName,
9
+ NotificationEvents,
10
+ } from './types';
11
+
12
+ /// Manager for recording notifications with controls.
13
+ class RecordingNotificationManager
14
+ implements
15
+ NotificationManager<
16
+ RecordingNotificationInfo,
17
+ RecordingNotificationInfo,
18
+ RecordingNotificationEventName
19
+ >
20
+ {
21
+ private notificationKey = 'recording';
22
+ private isRegistered = false;
23
+ private isShown = false;
24
+ private audioEventEmitter: AudioEventEmitter;
25
+ private isIOS = Platform.OS === 'ios';
26
+
27
+ constructor() {
28
+ this.audioEventEmitter = new AudioEventEmitter(global.AudioEventEmitter);
29
+ }
30
+
31
+ /// Register the recording notification (must be called before showing).
32
+ async register(): Promise<void> {
33
+ if (this.isRegistered) {
34
+ console.warn('RecordingNotification is already registered');
35
+ return;
36
+ }
37
+
38
+ // Recording notifications are only supported on Android
39
+ if (this.isIOS) {
40
+ this.isRegistered = true;
41
+ return;
42
+ }
43
+
44
+ if (!NativeAudioAPIModule) {
45
+ throw new Error('NativeAudioAPIModule is not available');
46
+ }
47
+
48
+ const result = await NativeAudioAPIModule.registerNotification(
49
+ 'recording',
50
+ this.notificationKey
51
+ );
52
+
53
+ if (result.error) {
54
+ throw new Error(result.error);
55
+ }
56
+
57
+ this.isRegistered = true;
58
+ }
59
+
60
+ /// Show the notification with initial metadata.
61
+ async show(info: RecordingNotificationInfo): Promise<void> {
62
+ if (!this.isRegistered) {
63
+ throw new Error(
64
+ 'RecordingNotification must be registered before showing. Call register() first.'
65
+ );
66
+ }
67
+
68
+ // Recording notifications are only supported on Android
69
+ if (this.isIOS) {
70
+ this.isShown = true;
71
+ return;
72
+ }
73
+
74
+ if (!NativeAudioAPIModule) {
75
+ throw new Error('NativeAudioAPIModule is not available');
76
+ }
77
+
78
+ const result = await NativeAudioAPIModule.showNotification(
79
+ this.notificationKey,
80
+ info as Record<string, string | number | boolean | undefined>
81
+ );
82
+
83
+ if (result.error) {
84
+ throw new Error(result.error);
85
+ }
86
+
87
+ this.isShown = true;
88
+ }
89
+
90
+ /// Update the notification with new metadata or state.
91
+ async update(info: RecordingNotificationInfo): Promise<void> {
92
+ if (!this.isShown) {
93
+ console.warn('RecordingNotification is not shown. Call show() first.');
94
+ return;
95
+ }
96
+
97
+ // Recording notifications are only supported on Android
98
+ if (this.isIOS) {
99
+ return;
100
+ }
101
+
102
+ if (!NativeAudioAPIModule) {
103
+ throw new Error('NativeAudioAPIModule is not available');
104
+ }
105
+
106
+ const result = await NativeAudioAPIModule.updateNotification(
107
+ this.notificationKey,
108
+ info as Record<string, string | number | boolean | undefined>
109
+ );
110
+
111
+ if (result.error) {
112
+ throw new Error(result.error);
113
+ }
114
+ }
115
+
116
+ /// Hide the notification (can be shown again later).
117
+ async hide(): Promise<void> {
118
+ if (!this.isShown) {
119
+ return;
120
+ }
121
+
122
+ // Recording notifications are only supported on Android
123
+ if (this.isIOS) {
124
+ this.isShown = false;
125
+ return;
126
+ }
127
+
128
+ if (!NativeAudioAPIModule) {
129
+ throw new Error('NativeAudioAPIModule is not available');
130
+ }
131
+
132
+ const result = await NativeAudioAPIModule.hideNotification(
133
+ this.notificationKey
134
+ );
135
+
136
+ if (result.error) {
137
+ throw new Error(result.error);
138
+ }
139
+
140
+ this.isShown = false;
141
+ }
142
+
143
+ /// Unregister the notification (must register again to use).
144
+ async unregister(): Promise<void> {
145
+ if (!this.isRegistered) {
146
+ return;
147
+ }
148
+
149
+ if (this.isShown) {
150
+ await this.hide();
151
+ }
152
+
153
+ // Recording notifications are only supported on Android
154
+ if (this.isIOS) {
155
+ this.isRegistered = false;
156
+ return;
157
+ }
158
+
159
+ if (!NativeAudioAPIModule) {
160
+ throw new Error('NativeAudioAPIModule is not available');
161
+ }
162
+
163
+ const result = await NativeAudioAPIModule.unregisterNotification(
164
+ this.notificationKey
165
+ );
166
+
167
+ if (result.error) {
168
+ throw new Error(result.error);
169
+ }
170
+
171
+ this.isRegistered = false;
172
+ }
173
+
174
+ /// Enable or disable a specific recording control.
175
+ async enableControl(
176
+ control: RecordingControlName,
177
+ enabled: boolean
178
+ ): Promise<void> {
179
+ if (!this.isRegistered) {
180
+ console.warn('RecordingNotification is not registered');
181
+ return;
182
+ }
183
+
184
+ // Recording notifications are only supported on Android
185
+ if (this.isIOS) {
186
+ return;
187
+ }
188
+
189
+ if (!NativeAudioAPIModule) {
190
+ throw new Error('NativeAudioAPIModule is not available');
191
+ }
192
+
193
+ const params = { control, enabled };
194
+ const result = await NativeAudioAPIModule.updateNotification(
195
+ this.notificationKey,
196
+ params as Record<string, string | number | boolean | undefined>
197
+ );
198
+
199
+ if (result.error) {
200
+ throw new Error(result.error);
201
+ }
202
+ }
203
+
204
+ /// Check if the notification is currently active.
205
+ async isActive(): Promise<boolean> {
206
+ // Recording notifications are only supported on Android
207
+ if (this.isIOS) {
208
+ return this.isShown;
209
+ }
210
+
211
+ if (!NativeAudioAPIModule) {
212
+ return false;
213
+ }
214
+
215
+ return await NativeAudioAPIModule.isNotificationActive(
216
+ this.notificationKey
217
+ );
218
+ }
219
+
220
+ /// Add an event listener for notification actions.
221
+ addEventListener<T extends RecordingNotificationEventName>(
222
+ eventName: T,
223
+ callback: (event: NotificationEvents[T]) => void
224
+ ): AudioEventSubscription {
225
+ // Recording notifications are only supported on Android
226
+ if (this.isIOS) {
227
+ // Return a dummy subscription for iOS
228
+ return {
229
+ remove: () => {},
230
+ } as unknown as AudioEventSubscription;
231
+ }
232
+
233
+ return this.audioEventEmitter.addAudioEventListener(eventName, callback);
234
+ }
235
+
236
+ /** Remove an event listener. */
237
+ removeEventListener(subscription: AudioEventSubscription): void {
238
+ subscription.remove();
239
+ }
240
+ }
241
+
242
+ export default new RecordingNotificationManager();