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
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { AudioEventSubscription } from '../../events';
|
|
2
|
+
import { EventEmptyType, EventTypeWithValue } from '../../events/types';
|
|
3
|
+
|
|
4
|
+
/// Generic notification manager interface that all notification managers should implement.
|
|
5
|
+
/// Provides a consistent API for managing notification lifecycle and events.
|
|
6
|
+
export interface NotificationManager<
|
|
7
|
+
TShowOptions,
|
|
8
|
+
TUpdateOptions,
|
|
9
|
+
TEventName extends NotificationEventName,
|
|
10
|
+
> {
|
|
11
|
+
/// Register the notification (must be called before showing).
|
|
12
|
+
register(): Promise<void>;
|
|
13
|
+
|
|
14
|
+
/// Show the notification with initial options.
|
|
15
|
+
show(options: TShowOptions): Promise<void>;
|
|
16
|
+
|
|
17
|
+
/// Update the notification with new options.
|
|
18
|
+
update(options: TUpdateOptions): Promise<void>;
|
|
19
|
+
|
|
20
|
+
/// Hide the notification (can be shown again later).
|
|
21
|
+
hide(): Promise<void>;
|
|
22
|
+
|
|
23
|
+
/// Unregister the notification (must register again to use).
|
|
24
|
+
unregister(): Promise<void>;
|
|
25
|
+
|
|
26
|
+
/// Check if the notification is currently active.
|
|
27
|
+
isActive(): Promise<boolean>;
|
|
28
|
+
|
|
29
|
+
/// Add an event listener for notification events.
|
|
30
|
+
addEventListener<T extends TEventName>(
|
|
31
|
+
eventName: T,
|
|
32
|
+
callback: NotificationCallback<T>
|
|
33
|
+
): AudioEventSubscription;
|
|
34
|
+
|
|
35
|
+
/// Remove an event listener.
|
|
36
|
+
removeEventListener(subscription: AudioEventSubscription): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Metadata and state information for playback notifications.
|
|
40
|
+
export interface PlaybackNotificationInfo {
|
|
41
|
+
title?: string;
|
|
42
|
+
artist?: string;
|
|
43
|
+
album?: string;
|
|
44
|
+
artwork?: string | { uri: string };
|
|
45
|
+
androidSmallIcon?: string | { uri: string };
|
|
46
|
+
duration?: number;
|
|
47
|
+
elapsedTime?: number;
|
|
48
|
+
speed?: number;
|
|
49
|
+
state?: 'playing' | 'paused';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Available playback control actions.
|
|
53
|
+
export type PlaybackControlName =
|
|
54
|
+
| 'play'
|
|
55
|
+
| 'pause'
|
|
56
|
+
| 'next'
|
|
57
|
+
| 'previous'
|
|
58
|
+
| 'skipForward'
|
|
59
|
+
| 'skipBackward'
|
|
60
|
+
| 'seekTo';
|
|
61
|
+
|
|
62
|
+
/// Event names for playback notification actions.
|
|
63
|
+
interface PlaybackNotificationEvent {
|
|
64
|
+
playbackNotificationPlay: EventEmptyType;
|
|
65
|
+
playbackNotificationPause: EventEmptyType;
|
|
66
|
+
playbackNotificationNext: EventEmptyType;
|
|
67
|
+
playbackNotificationPrevious: EventEmptyType;
|
|
68
|
+
playbackNotificationSkipForward: EventTypeWithValue;
|
|
69
|
+
playbackNotificationSkipBackward: EventTypeWithValue;
|
|
70
|
+
playbackNotificationSeekTo: EventTypeWithValue;
|
|
71
|
+
playbackNotificationDismissed: EventEmptyType;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type PlaybackNotificationEventName = keyof PlaybackNotificationEvent;
|
|
75
|
+
|
|
76
|
+
/// Metadata and state information for recording notifications.
|
|
77
|
+
export interface RecordingNotificationInfo {
|
|
78
|
+
title?: string;
|
|
79
|
+
description?: string;
|
|
80
|
+
artwork?: string | { uri: string };
|
|
81
|
+
state?: 'recording' | 'stopped';
|
|
82
|
+
control?: RecordingControlName;
|
|
83
|
+
enabled?: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// Available recording control actions.
|
|
87
|
+
export type RecordingControlName = 'start' | 'stop';
|
|
88
|
+
|
|
89
|
+
/// Event names for recording notification actions.
|
|
90
|
+
interface RecordingNotificationEvent {
|
|
91
|
+
recordingNotificationStart: EventEmptyType;
|
|
92
|
+
recordingNotificationStop: EventEmptyType;
|
|
93
|
+
recordingNotificationDismissed: EventEmptyType;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type RecordingNotificationEventName = keyof RecordingNotificationEvent;
|
|
97
|
+
|
|
98
|
+
export type NotificationEvents = PlaybackNotificationEvent &
|
|
99
|
+
RecordingNotificationEvent;
|
|
100
|
+
|
|
101
|
+
export type NotificationEventName = keyof NotificationEvents;
|
|
102
|
+
|
|
103
|
+
export type NotificationCallback<Name extends NotificationEventName> = (
|
|
104
|
+
event: NotificationEvents[Name]
|
|
105
|
+
) => void;
|
|
106
|
+
|
|
107
|
+
/// Options for a simple notification with title and text.
|
|
108
|
+
export interface SimpleNotificationOptions {
|
|
109
|
+
title?: string;
|
|
110
|
+
text?: string;
|
|
111
|
+
}
|
package/src/system/types.ts
CHANGED
|
@@ -34,24 +34,6 @@ export interface SessionOptions {
|
|
|
34
34
|
iosAllowHaptics?: boolean;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export type MediaState = 'state_playing' | 'state_paused';
|
|
38
|
-
|
|
39
|
-
interface BaseLockScreenInfo {
|
|
40
|
-
[key: string]: string | boolean | number | undefined;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface LockScreenInfo extends BaseLockScreenInfo {
|
|
44
|
-
title?: string;
|
|
45
|
-
artwork?: string;
|
|
46
|
-
artist?: string;
|
|
47
|
-
album?: string;
|
|
48
|
-
duration?: number;
|
|
49
|
-
description?: string; // android only
|
|
50
|
-
state?: MediaState;
|
|
51
|
-
speed?: number;
|
|
52
|
-
elapsedTime?: number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
37
|
export type PermissionStatus = 'Undetermined' | 'Denied' | 'Granted';
|
|
56
38
|
|
|
57
39
|
export interface AudioDeviceInfo {
|
package/src/types.ts
CHANGED
|
@@ -44,23 +44,29 @@ export interface OfflineAudioContextOptions {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export enum FileDirectory {
|
|
47
|
-
Document =
|
|
48
|
-
Cache =
|
|
47
|
+
Document = 0,
|
|
48
|
+
Cache = 1,
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export enum FileFormat {
|
|
52
|
-
Wav =
|
|
53
|
-
Caf =
|
|
54
|
-
M4A =
|
|
55
|
-
Flac =
|
|
52
|
+
Wav = 0,
|
|
53
|
+
Caf = 1,
|
|
54
|
+
M4A = 2,
|
|
55
|
+
Flac = 3,
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export enum IOSAudioQuality {
|
|
59
|
-
Min =
|
|
60
|
-
Low =
|
|
61
|
-
Medium =
|
|
62
|
-
High =
|
|
63
|
-
Max =
|
|
59
|
+
Min = 0,
|
|
60
|
+
Low = 1,
|
|
61
|
+
Medium = 2,
|
|
62
|
+
High = 3,
|
|
63
|
+
Max = 4,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export enum BitDepth {
|
|
67
|
+
Bit16 = 0,
|
|
68
|
+
Bit24 = 1,
|
|
69
|
+
Bit32 = 2,
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
export enum FlacCompressionLevel {
|
|
@@ -75,12 +81,6 @@ export enum FlacCompressionLevel {
|
|
|
75
81
|
L8 = 8,
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
export enum BitDepth {
|
|
79
|
-
Bit16 = 1,
|
|
80
|
-
Bit24 = 2,
|
|
81
|
-
Bit32 = 3,
|
|
82
|
-
}
|
|
83
|
-
|
|
84
84
|
export interface FilePresetType {
|
|
85
85
|
bitRate: number;
|
|
86
86
|
sampleRate: number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './notification';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable no-useless-constructor */
|
|
3
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
4
|
+
|
|
5
|
+
import type { AudioEventSubscription } from '../../events';
|
|
6
|
+
import type {
|
|
7
|
+
NotificationManager,
|
|
8
|
+
PlaybackNotificationInfo,
|
|
9
|
+
PlaybackControlName,
|
|
10
|
+
PlaybackNotificationEventName,
|
|
11
|
+
NotificationEvents,
|
|
12
|
+
} from '../../system';
|
|
13
|
+
|
|
14
|
+
/// Mock Manager for playback notifications. Does nothing.
|
|
15
|
+
class PlaybackNotificationManager
|
|
16
|
+
implements
|
|
17
|
+
NotificationManager<
|
|
18
|
+
PlaybackNotificationInfo,
|
|
19
|
+
PlaybackNotificationInfo,
|
|
20
|
+
PlaybackNotificationEventName
|
|
21
|
+
>
|
|
22
|
+
{
|
|
23
|
+
private isRegistered = false;
|
|
24
|
+
private isShown = false;
|
|
25
|
+
|
|
26
|
+
constructor() {}
|
|
27
|
+
|
|
28
|
+
async register(): Promise<void> {}
|
|
29
|
+
|
|
30
|
+
async show(info: PlaybackNotificationInfo): Promise<void> {}
|
|
31
|
+
|
|
32
|
+
async update(info: PlaybackNotificationInfo): Promise<void> {}
|
|
33
|
+
|
|
34
|
+
async hide(): Promise<void> {}
|
|
35
|
+
|
|
36
|
+
async unregister(): Promise<void> {}
|
|
37
|
+
|
|
38
|
+
async enableControl(
|
|
39
|
+
control: PlaybackControlName,
|
|
40
|
+
enabled: boolean
|
|
41
|
+
): Promise<void> {}
|
|
42
|
+
|
|
43
|
+
async isActive(): Promise<boolean> {
|
|
44
|
+
return this.isShown;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
addEventListener<T extends PlaybackNotificationEventName>(
|
|
48
|
+
eventName: T,
|
|
49
|
+
callback: (event: NotificationEvents[T]) => void
|
|
50
|
+
): AudioEventSubscription {
|
|
51
|
+
// dummy subscription object with a no-op remove method
|
|
52
|
+
return {
|
|
53
|
+
remove: () => {},
|
|
54
|
+
} as unknown as AudioEventSubscription;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
removeEventListener(subscription: AudioEventSubscription): void {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default new PlaybackNotificationManager();
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable no-useless-constructor */
|
|
3
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
4
|
+
|
|
5
|
+
import type { AudioEventSubscription } from '../../events';
|
|
6
|
+
import type {
|
|
7
|
+
NotificationManager,
|
|
8
|
+
RecordingNotificationInfo,
|
|
9
|
+
RecordingControlName,
|
|
10
|
+
RecordingNotificationEventName,
|
|
11
|
+
NotificationEvents,
|
|
12
|
+
} from '../../system';
|
|
13
|
+
|
|
14
|
+
/// Mock Manager for recording notifications. Does nothing.
|
|
15
|
+
class RecordingNotificationManager
|
|
16
|
+
implements
|
|
17
|
+
NotificationManager<
|
|
18
|
+
RecordingNotificationInfo,
|
|
19
|
+
RecordingNotificationInfo,
|
|
20
|
+
RecordingNotificationEventName
|
|
21
|
+
>
|
|
22
|
+
{
|
|
23
|
+
private isRegistered = false;
|
|
24
|
+
private isShown = false;
|
|
25
|
+
|
|
26
|
+
constructor() {}
|
|
27
|
+
|
|
28
|
+
async register(): Promise<void> {}
|
|
29
|
+
|
|
30
|
+
async show(info: RecordingNotificationInfo): Promise<void> {}
|
|
31
|
+
|
|
32
|
+
async update(info: RecordingNotificationInfo): Promise<void> {}
|
|
33
|
+
|
|
34
|
+
async hide(): Promise<void> {}
|
|
35
|
+
|
|
36
|
+
async unregister(): Promise<void> {}
|
|
37
|
+
|
|
38
|
+
async enableControl(
|
|
39
|
+
control: RecordingControlName,
|
|
40
|
+
enabled: boolean
|
|
41
|
+
): Promise<void> {}
|
|
42
|
+
|
|
43
|
+
async isActive(): Promise<boolean> {
|
|
44
|
+
return this.isShown;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
addEventListener<T extends RecordingNotificationEventName>(
|
|
48
|
+
eventName: T,
|
|
49
|
+
callback: (event: NotificationEvents[T]) => void
|
|
50
|
+
): AudioEventSubscription {
|
|
51
|
+
// dummy subscription object with a no-op remove method
|
|
52
|
+
return {
|
|
53
|
+
remove: () => {},
|
|
54
|
+
} as unknown as AudioEventSubscription;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
removeEventListener(subscription: AudioEventSubscription): void {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export default new RecordingNotificationManager();
|
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
package com.swmansion.audioapi.system
|
|
2
|
-
|
|
3
|
-
import android.graphics.Bitmap
|
|
4
|
-
import android.graphics.BitmapFactory
|
|
5
|
-
import android.graphics.drawable.BitmapDrawable
|
|
6
|
-
import android.support.v4.media.MediaMetadataCompat
|
|
7
|
-
import android.support.v4.media.session.MediaSessionCompat
|
|
8
|
-
import android.support.v4.media.session.PlaybackStateCompat
|
|
9
|
-
import android.util.Log
|
|
10
|
-
import androidx.core.app.NotificationCompat
|
|
11
|
-
import androidx.media.app.NotificationCompat.MediaStyle
|
|
12
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
|
-
import com.facebook.react.bridge.ReadableMap
|
|
14
|
-
import com.facebook.react.bridge.ReadableType
|
|
15
|
-
import com.swmansion.audioapi.R
|
|
16
|
-
import java.io.IOException
|
|
17
|
-
import java.lang.ref.WeakReference
|
|
18
|
-
import java.net.URL
|
|
19
|
-
|
|
20
|
-
class LockScreenManager(
|
|
21
|
-
private val reactContext: WeakReference<ReactApplicationContext>,
|
|
22
|
-
private val mediaSession: WeakReference<MediaSessionCompat>,
|
|
23
|
-
private val mediaNotificationManager: WeakReference<MediaNotificationManager>,
|
|
24
|
-
) {
|
|
25
|
-
private var pb: PlaybackStateCompat.Builder = PlaybackStateCompat.Builder()
|
|
26
|
-
private var state: PlaybackStateCompat = pb.build()
|
|
27
|
-
private var controls: Long = 0
|
|
28
|
-
var isPlaying: Boolean = false
|
|
29
|
-
|
|
30
|
-
private var nb: NotificationCompat.Builder = NotificationCompat.Builder(reactContext.get()!!, MediaSessionManager.CHANNEL_ID)
|
|
31
|
-
|
|
32
|
-
private var artworkThread: Thread? = null
|
|
33
|
-
|
|
34
|
-
private var title: String? = null
|
|
35
|
-
private var artist: String? = null
|
|
36
|
-
private var album: String? = null
|
|
37
|
-
private var description: String? = null
|
|
38
|
-
private var duration: Long = 0L
|
|
39
|
-
private var speed: Float = 1.0F
|
|
40
|
-
private var elapsedTime: Long = 0L
|
|
41
|
-
private var artwork: String? = null
|
|
42
|
-
private var playbackState: Int = PlaybackStateCompat.STATE_PAUSED
|
|
43
|
-
|
|
44
|
-
init {
|
|
45
|
-
pb.setActions(controls)
|
|
46
|
-
nb.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
47
|
-
nb.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
48
|
-
|
|
49
|
-
updateNotificationMediaStyle()
|
|
50
|
-
mediaNotificationManager.get()?.updateActions(controls)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
fun setLockScreenInfo(info: ReadableMap?) {
|
|
54
|
-
if (artworkThread != null && artworkThread!!.isAlive) {
|
|
55
|
-
artworkThread!!.interrupt()
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
artworkThread = null
|
|
59
|
-
|
|
60
|
-
if (info == null) {
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
val md = MediaMetadataCompat.Builder()
|
|
65
|
-
|
|
66
|
-
if (info.hasKey("title")) {
|
|
67
|
-
title = info.getString("title")
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (info.hasKey("artist")) {
|
|
71
|
-
artist = info.getString("artist")
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (info.hasKey("album")) {
|
|
75
|
-
album = info.getString("album")
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (info.hasKey("description")) {
|
|
79
|
-
description = info.getString("description")
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (info.hasKey("duration")) {
|
|
83
|
-
duration = (info.getDouble("duration") * 1000).toLong()
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
md.putText(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
|
87
|
-
md.putText(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
|
|
88
|
-
md.putText(MediaMetadataCompat.METADATA_KEY_ALBUM, album)
|
|
89
|
-
md.putText(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, description)
|
|
90
|
-
md.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration)
|
|
91
|
-
|
|
92
|
-
nb.setContentTitle(title)
|
|
93
|
-
nb.setContentText(artist)
|
|
94
|
-
nb.setContentInfo(album)
|
|
95
|
-
|
|
96
|
-
if (info.hasKey("artwork")) {
|
|
97
|
-
var localArtwork = false
|
|
98
|
-
|
|
99
|
-
if (info.getType("artwork") == ReadableType.Map) {
|
|
100
|
-
artwork = info.getMap("artwork")?.getString("uri")
|
|
101
|
-
localArtwork = true
|
|
102
|
-
} else {
|
|
103
|
-
artwork = info.getString("artwork")
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
val artworkLocal = localArtwork
|
|
107
|
-
|
|
108
|
-
artworkThread =
|
|
109
|
-
Thread {
|
|
110
|
-
try {
|
|
111
|
-
val bitmap: Bitmap? = artwork?.let { loadArtwork(it, artworkLocal) }
|
|
112
|
-
|
|
113
|
-
val currentMetadata = mediaSession.get()?.controller?.metadata
|
|
114
|
-
val newBuilder =
|
|
115
|
-
MediaMetadataCompat.Builder(
|
|
116
|
-
currentMetadata,
|
|
117
|
-
)
|
|
118
|
-
mediaSession.get()?.setMetadata(
|
|
119
|
-
newBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap).build(),
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
nb.setLargeIcon(bitmap)
|
|
123
|
-
mediaNotificationManager.get()?.updateNotification(nb, isPlaying)
|
|
124
|
-
|
|
125
|
-
artworkThread = null
|
|
126
|
-
} catch (ex: Exception) {
|
|
127
|
-
ex.printStackTrace()
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
artworkThread!!.start()
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
speed =
|
|
134
|
-
if (info.hasKey("speed")) {
|
|
135
|
-
info.getDouble("speed").toFloat()
|
|
136
|
-
} else {
|
|
137
|
-
state.playbackSpeed
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (isPlaying && speed == 0F) {
|
|
141
|
-
speed = 1F
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
elapsedTime =
|
|
145
|
-
if (info.hasKey("elapsedTime")) {
|
|
146
|
-
(info.getDouble("elapsedTime") * 1000).toLong()
|
|
147
|
-
} else {
|
|
148
|
-
state.position
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (info.hasKey("state")) {
|
|
152
|
-
val state = info.getString("state")
|
|
153
|
-
|
|
154
|
-
when (state) {
|
|
155
|
-
"state_playing" -> {
|
|
156
|
-
this.playbackState = PlaybackStateCompat.STATE_PLAYING
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
"state_paused" -> {
|
|
160
|
-
this.playbackState = PlaybackStateCompat.STATE_PAUSED
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
updatePlaybackState(this.playbackState)
|
|
166
|
-
|
|
167
|
-
mediaSession.get()?.setMetadata(md.build())
|
|
168
|
-
mediaSession.get()?.setActive(true)
|
|
169
|
-
mediaNotificationManager.get()?.updateNotification(nb, isPlaying)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
fun resetLockScreenInfo() {
|
|
173
|
-
if (artworkThread != null && artworkThread!!.isAlive) artworkThread!!.interrupt()
|
|
174
|
-
artworkThread = null
|
|
175
|
-
|
|
176
|
-
title = null
|
|
177
|
-
artist = null
|
|
178
|
-
album = null
|
|
179
|
-
description = null
|
|
180
|
-
duration = 0L
|
|
181
|
-
speed = 1.0F
|
|
182
|
-
elapsedTime = 0L
|
|
183
|
-
artwork = null
|
|
184
|
-
playbackState = PlaybackStateCompat.STATE_PAUSED
|
|
185
|
-
isPlaying = false
|
|
186
|
-
|
|
187
|
-
val emptyMetadata = MediaMetadataCompat.Builder().build()
|
|
188
|
-
mediaSession.get()?.setMetadata(emptyMetadata)
|
|
189
|
-
|
|
190
|
-
pb.setState(PlaybackStateCompat.STATE_NONE, 0, 0f)
|
|
191
|
-
pb.setActions(controls)
|
|
192
|
-
state = pb.build()
|
|
193
|
-
mediaSession.get()?.setPlaybackState(state)
|
|
194
|
-
mediaSession.get()?.setActive(false)
|
|
195
|
-
|
|
196
|
-
nb.setContentTitle("")
|
|
197
|
-
nb.setContentText("")
|
|
198
|
-
nb.setContentInfo("")
|
|
199
|
-
|
|
200
|
-
mediaNotificationManager.get()?.updateNotification(nb, isPlaying)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
fun enableRemoteCommand(
|
|
204
|
-
name: String,
|
|
205
|
-
enabled: Boolean,
|
|
206
|
-
) {
|
|
207
|
-
pb = PlaybackStateCompat.Builder()
|
|
208
|
-
var controlValue = 0L
|
|
209
|
-
when (name) {
|
|
210
|
-
"remotePlay" -> controlValue = PlaybackStateCompat.ACTION_PLAY
|
|
211
|
-
"remotePause" -> controlValue = PlaybackStateCompat.ACTION_PAUSE
|
|
212
|
-
"remoteStop" -> controlValue = PlaybackStateCompat.ACTION_STOP
|
|
213
|
-
"remoteTogglePlayPause" -> controlValue = PlaybackStateCompat.ACTION_PLAY_PAUSE
|
|
214
|
-
"remoteNextTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_NEXT
|
|
215
|
-
"remotePreviousTrack" -> controlValue = PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
|
|
216
|
-
"remoteSkipForward" -> controlValue = PlaybackStateCompat.ACTION_FAST_FORWARD
|
|
217
|
-
"remoteSkipBackward" -> controlValue = PlaybackStateCompat.ACTION_REWIND
|
|
218
|
-
"remoteChangePlaybackPosition" -> controlValue = PlaybackStateCompat.ACTION_SEEK_TO
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
controls =
|
|
222
|
-
if (enabled) {
|
|
223
|
-
controls or controlValue
|
|
224
|
-
} else {
|
|
225
|
-
controls and controlValue.inv()
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
mediaNotificationManager.get()?.updateActions(controls)
|
|
229
|
-
|
|
230
|
-
if (hasControl(PlaybackStateCompat.ACTION_REWIND)) {
|
|
231
|
-
pb.addCustomAction(
|
|
232
|
-
PlaybackStateCompat.CustomAction
|
|
233
|
-
.Builder(
|
|
234
|
-
"SkipBackward",
|
|
235
|
-
"Skip Backward",
|
|
236
|
-
R.drawable.skip_backward_15,
|
|
237
|
-
).build(),
|
|
238
|
-
)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
pb.setActions(controls)
|
|
242
|
-
|
|
243
|
-
if (hasControl(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
|
|
244
|
-
pb.addCustomAction(
|
|
245
|
-
PlaybackStateCompat.CustomAction
|
|
246
|
-
.Builder(
|
|
247
|
-
"SkipForward",
|
|
248
|
-
"Skip Forward",
|
|
249
|
-
R.drawable.skip_forward_15,
|
|
250
|
-
).build(),
|
|
251
|
-
)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
state = pb.build()
|
|
255
|
-
mediaSession.get()?.setPlaybackState(state)
|
|
256
|
-
|
|
257
|
-
updateNotificationMediaStyle()
|
|
258
|
-
|
|
259
|
-
if (mediaSession.get()?.isActive == true) {
|
|
260
|
-
mediaNotificationManager.get()?.updateNotification(nb, isPlaying)
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
private fun loadArtwork(
|
|
265
|
-
url: String,
|
|
266
|
-
local: Boolean,
|
|
267
|
-
): Bitmap? {
|
|
268
|
-
var bitmap: Bitmap? = null
|
|
269
|
-
|
|
270
|
-
try {
|
|
271
|
-
// If we are running the app in debug mode, the "local" image will be served from htt://localhost:8080, so we need to check for this case and load those images from URL
|
|
272
|
-
if (local && !url.startsWith("http")) {
|
|
273
|
-
// Gets the drawable from the RN's helper for local resources
|
|
274
|
-
val helper = com.facebook.react.views.imagehelper.ResourceDrawableIdHelper.instance
|
|
275
|
-
val image = helper.getResourceDrawable(reactContext.get()!!, url)
|
|
276
|
-
|
|
277
|
-
bitmap =
|
|
278
|
-
if (image is BitmapDrawable) {
|
|
279
|
-
image.bitmap
|
|
280
|
-
} else {
|
|
281
|
-
BitmapFactory.decodeFile(url)
|
|
282
|
-
}
|
|
283
|
-
} else {
|
|
284
|
-
// Open connection to the URL and decodes the image
|
|
285
|
-
val con = URL(url).openConnection()
|
|
286
|
-
con.connect()
|
|
287
|
-
val input = con.getInputStream()
|
|
288
|
-
bitmap = BitmapFactory.decodeStream(input)
|
|
289
|
-
input.close()
|
|
290
|
-
}
|
|
291
|
-
} catch (ex: IOException) {
|
|
292
|
-
Log.w("MediaSessionManager", "Could not load the artwork", ex)
|
|
293
|
-
} catch (ex: IndexOutOfBoundsException) {
|
|
294
|
-
Log.w("MediaSessionManager", "Could not load the artwork", ex)
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return bitmap
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private fun updatePlaybackState(playbackState: Int) {
|
|
301
|
-
isPlaying = playbackState == PlaybackStateCompat.STATE_PLAYING
|
|
302
|
-
|
|
303
|
-
pb.setState(playbackState, elapsedTime, speed)
|
|
304
|
-
pb.setActions(controls)
|
|
305
|
-
state = pb.build()
|
|
306
|
-
mediaSession.get()?.setPlaybackState(state)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private fun hasControl(control: Long): Boolean = (controls and control) == control
|
|
310
|
-
|
|
311
|
-
private fun updateNotificationMediaStyle() {
|
|
312
|
-
val style = MediaStyle()
|
|
313
|
-
style.setMediaSession(mediaSession.get()?.sessionToken)
|
|
314
|
-
var controlCount = 0
|
|
315
|
-
if (hasControl(PlaybackStateCompat.ACTION_PLAY) ||
|
|
316
|
-
hasControl(PlaybackStateCompat.ACTION_PAUSE) ||
|
|
317
|
-
hasControl(
|
|
318
|
-
PlaybackStateCompat.ACTION_PLAY_PAUSE,
|
|
319
|
-
)
|
|
320
|
-
) {
|
|
321
|
-
controlCount += 1
|
|
322
|
-
}
|
|
323
|
-
if (hasControl(PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) {
|
|
324
|
-
controlCount += 1
|
|
325
|
-
}
|
|
326
|
-
if (hasControl(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) {
|
|
327
|
-
controlCount += 1
|
|
328
|
-
}
|
|
329
|
-
if (hasControl(PlaybackStateCompat.ACTION_FAST_FORWARD)) {
|
|
330
|
-
controlCount += 1
|
|
331
|
-
}
|
|
332
|
-
if (hasControl(PlaybackStateCompat.ACTION_REWIND)) {
|
|
333
|
-
controlCount += 1
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (hasControl(PlaybackStateCompat.ACTION_SEEK_TO)) {
|
|
337
|
-
controlCount += 1
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
val actions = IntArray(controlCount)
|
|
341
|
-
for (i in actions.indices) {
|
|
342
|
-
actions[i] = i
|
|
343
|
-
}
|
|
344
|
-
style.setShowActionsInCompactView(*actions)
|
|
345
|
-
nb.setStyle(style)
|
|
346
|
-
}
|
|
347
|
-
}
|