react-native-nitro-player 1.7.0-alpha.3 → 1.7.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/README.md +9 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +51 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +45 -7
- package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +17 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/NitroPlayerLogger.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +41 -31
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAudioFocus.kt +65 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCast.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +92 -15
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +12 -17
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +5 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +39 -13
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +25 -4
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerRecovery.kt +97 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +0 -5
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +22 -5
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +3 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/TrackItemJson.kt +6 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/AudioFocusController.kt +87 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/CastSessionController.kt +2 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/ExoPlayerBuilder.kt +3 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaBrowserService.kt +96 -45
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibrary.kt +2 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryManager.kt +147 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryParser.kt +3 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionCallbackFactory.kt +102 -24
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +51 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +82 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +75 -0
- package/ios/cast/CastQueueDiff.swift +58 -0
- package/ios/cast/CastQueueReconciler.swift +228 -0
- package/ios/core/CastSessionManager.swift +215 -31
- package/ios/core/TrackPlayerCast.swift +96 -65
- package/ios/core/TrackPlayerCore.swift +31 -0
- package/ios/core/TrackPlayerListener.swift +19 -3
- package/ios/core/TrackPlayerPlayback.swift +12 -10
- package/ios/core/TrackPlayerQueue.swift +10 -2
- package/ios/core/TrackPlayerQueueBuild.swift +21 -9
- package/ios/core/TrackPlayerTempQueue.swift +27 -15
- package/ios/core/TrackPlayerUrlLoader.swift +6 -2
- package/ios/playlist/PlaylistManager.swift +60 -1
- package/ios/queue/HybridPlayerQueue.swift +40 -13
- package/lib/hooks/callbackManager.d.ts +2 -0
- package/lib/hooks/callbackManager.js +23 -8
- package/lib/index.js +5 -1
- package/lib/specs/AndroidAutoMediaLibrary.nitro.d.ts +12 -0
- package/lib/specs/TrackPlayer.nitro.d.ts +5 -1
- package/lib/types/AndroidAutoMediaLibrary.d.ts +11 -0
- package/lib/types/PlayerQueue.d.ts +14 -0
- package/lib/utils/androidAutoMediaLibrary.d.ts +13 -1
- package/lib/utils/androidAutoMediaLibrary.js +23 -0
- package/nitrogen/generated/android/NitroPlayerOnLoad.cpp +6 -0
- package/nitrogen/generated/android/c++/JAndroidAudioFocusMode.hpp +61 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.cpp +29 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.hpp +6 -0
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.cpp +43 -0
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +4 -0
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +9 -3
- package/nitrogen/generated/android/c++/JShufflePlaylistOptions.hpp +57 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/AndroidAudioFocusMode.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__string_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrarySpec.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridPlayerQueueSpec.kt +8 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +6 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/ShufflePlaylistOptions.kt +38 -0
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +36 -0
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Umbrella.hpp +6 -0
- package/nitrogen/generated/ios/c++/HybridPlayerQueueSpecSwift.hpp +19 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +3 -0
- package/nitrogen/generated/ios/swift/AndroidAudioFocusMode.swift +44 -0
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec_cxx.swift +38 -0
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +12 -1
- package/nitrogen/generated/ios/swift/ShufflePlaylistOptions.swift +42 -0
- package/nitrogen/generated/shared/c++/AndroidAudioFocusMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.cpp +6 -0
- package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.hpp +7 -0
- package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.hpp +5 -0
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +9 -3
- package/nitrogen/generated/shared/c++/ShufflePlaylistOptions.hpp +83 -0
- package/package.json +1 -1
- package/src/hooks/callbackManager.ts +25 -10
- package/src/index.ts +9 -1
- package/src/specs/AndroidAutoMediaLibrary.nitro.ts +18 -0
- package/src/specs/TrackPlayer.nitro.ts +11 -0
- package/src/types/AndroidAutoMediaLibrary.ts +24 -0
- package/src/types/PlayerQueue.ts +17 -0
- package/src/utils/androidAutoMediaLibrary.ts +48 -1
|
@@ -14,6 +14,7 @@ class PlaylistManager {
|
|
|
14
14
|
private var listeners: [(String, ([PlaylistModel], QueueOperation?) -> Void)] = []
|
|
15
15
|
private var playlistListeners: [String: [(String, (PlaylistModel, QueueOperation?) -> Void)]] =
|
|
16
16
|
[:]
|
|
17
|
+
private var anyPlaylistListeners: [(String, (PlaylistModel, QueueOperation?) -> Void)] = []
|
|
17
18
|
// Backing store must only be touched inside `queue`; every current use of the
|
|
18
19
|
// computed property is outside a queue.sync block, so the accessors can sync.
|
|
19
20
|
private var _currentPlaylistId: String?
|
|
@@ -257,6 +258,48 @@ class PlaylistManager {
|
|
|
257
258
|
return true
|
|
258
259
|
}
|
|
259
260
|
|
|
261
|
+
/// Removed count; nil when the playlist does not exist. Unknown ids are ignored.
|
|
262
|
+
func removeTracksFromPlaylist(playlistId: String, trackIds: [String]) -> Int? {
|
|
263
|
+
let ids = Set(trackIds)
|
|
264
|
+
let removed: Int? = queue.sync {
|
|
265
|
+
guard let playlist = playlists[playlistId] else { return nil }
|
|
266
|
+
let tracks = playlist.tracks.filter { !ids.contains($0.id) }
|
|
267
|
+
let count = playlist.tracks.count - tracks.count
|
|
268
|
+
if count > 0 {
|
|
269
|
+
playlists[playlistId] = PlaylistModel(
|
|
270
|
+
id: playlist.id, name: playlist.name, description: playlist.description,
|
|
271
|
+
artwork: playlist.artwork, tracks: tracks)
|
|
272
|
+
}
|
|
273
|
+
return count
|
|
274
|
+
}
|
|
275
|
+
if let removed, removed > 0 {
|
|
276
|
+
scheduleSave()
|
|
277
|
+
notifyPlaylistChanged(playlistId, .remove)
|
|
278
|
+
}
|
|
279
|
+
return removed
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/// Fisher-Yates; `firstTrackId` is pinned at index 0. Nil when the playlist does not exist.
|
|
283
|
+
func shufflePlaylist(playlistId: String, firstTrackId: String?) -> Bool? {
|
|
284
|
+
let changed: Bool? = queue.sync {
|
|
285
|
+
guard let playlist = playlists[playlistId] else { return nil }
|
|
286
|
+
var tracks = playlist.tracks
|
|
287
|
+
let pinned = tracks.firstIndex { $0.id == firstTrackId }.map { tracks.remove(at: $0) }
|
|
288
|
+
tracks.shuffle()
|
|
289
|
+
if let pinned { tracks.insert(pinned, at: 0) }
|
|
290
|
+
guard tracks.map(\.id) != playlist.tracks.map(\.id) else { return false }
|
|
291
|
+
playlists[playlistId] = PlaylistModel(
|
|
292
|
+
id: playlist.id, name: playlist.name, description: playlist.description,
|
|
293
|
+
artwork: playlist.artwork, tracks: tracks)
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
if changed == true {
|
|
297
|
+
scheduleSave()
|
|
298
|
+
notifyPlaylistChanged(playlistId, .update)
|
|
299
|
+
}
|
|
300
|
+
return changed
|
|
301
|
+
}
|
|
302
|
+
|
|
260
303
|
/**
|
|
261
304
|
* Load a playlist for playback (sets it as current)
|
|
262
305
|
*/
|
|
@@ -343,6 +386,11 @@ class PlaylistManager {
|
|
|
343
386
|
return trackIds.compactMap { foundTracks[$0] }
|
|
344
387
|
}
|
|
345
388
|
|
|
389
|
+
/// Mirrors the core's current playlist; playSong bypasses loadPlaylist.
|
|
390
|
+
func setCurrentPlaylistId(_ playlistId: String?) {
|
|
391
|
+
currentPlaylistId = playlistId
|
|
392
|
+
}
|
|
393
|
+
|
|
346
394
|
/**
|
|
347
395
|
* Get the current playlist ID
|
|
348
396
|
*/
|
|
@@ -403,11 +451,22 @@ class PlaylistManager {
|
|
|
403
451
|
currentListeners.forEach { $0.1(allPlaylists, operation) }
|
|
404
452
|
}
|
|
405
453
|
|
|
454
|
+
/// Fires for ANY playlist, including ones created after registration.
|
|
455
|
+
func addAnyPlaylistChangeListener(
|
|
456
|
+
listener: @escaping (PlaylistModel, QueueOperation?) -> Void
|
|
457
|
+
) -> () -> Void {
|
|
458
|
+
let listenerId = UUID().uuidString
|
|
459
|
+
queue.sync { anyPlaylistListeners.append((listenerId, listener)) }
|
|
460
|
+
return {
|
|
461
|
+
self.queue.sync { self.anyPlaylistListeners.removeAll { $0.0 == listenerId } }
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
406
465
|
private func notifyPlaylistChanged(_ playlistId: String, _ operation: QueueOperation?) {
|
|
407
466
|
let result: (PlaylistModel, [(String, (PlaylistModel, QueueOperation?) -> Void)])? = queue.sync
|
|
408
467
|
{
|
|
409
468
|
guard let p = playlists[playlistId] else { return nil }
|
|
410
|
-
return (p, playlistListeners[playlistId] ?? [])
|
|
469
|
+
return (p, (playlistListeners[playlistId] ?? []) + anyPlaylistListeners)
|
|
411
470
|
}
|
|
412
471
|
|
|
413
472
|
guard let (playlist, currentListeners) = result else { return }
|
|
@@ -73,14 +73,9 @@ final class HybridPlayerQueue: HybridPlayerQueueSpec {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/// Same semantics as the batch form: unknown ids are skipped, only a missing playlist rejects.
|
|
76
77
|
func removeTrackFromPlaylist(playlistId: String, trackId: String) throws -> Promise<Void> {
|
|
77
|
-
|
|
78
|
-
guard self.playlistManager.removeTrackFromPlaylist(playlistId: playlistId, trackId: trackId)
|
|
79
|
-
else {
|
|
80
|
-
throw RuntimeError.error(withMessage: "Track \(trackId) not found in playlist \(playlistId)")
|
|
81
|
-
}
|
|
82
|
-
self.core.updatePlaylist(playlistId: playlistId)
|
|
83
|
-
}
|
|
78
|
+
try removeTracksFromPlaylist(playlistId: playlistId, trackIds: [trackId])
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
func reorderTrackInPlaylist(playlistId: String, trackId: String, newIndex: Double) throws -> Promise<Void> {
|
|
@@ -94,6 +89,40 @@ final class HybridPlayerQueue: HybridPlayerQueueSpec {
|
|
|
94
89
|
}
|
|
95
90
|
}
|
|
96
91
|
|
|
92
|
+
/// On the player queue: the live queue must reflect the removal before the promise resolves.
|
|
93
|
+
func removeTracksFromPlaylist(playlistId: String, trackIds: [String]) throws -> Promise<Void> {
|
|
94
|
+
let promise = Promise<Void>()
|
|
95
|
+
core.playerQueue.async {
|
|
96
|
+
guard let removed = self.playlistManager.removeTracksFromPlaylist(playlistId: playlistId, trackIds: trackIds) else {
|
|
97
|
+
promise.reject(withError: RuntimeError.error(withMessage: "Playlist not found: \(playlistId)"))
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
if removed > 0, self.core.currentPlaylistId == playlistId {
|
|
101
|
+
self.core.syncCurrentPlaylistOnQueue(playlistId: playlistId)
|
|
102
|
+
}
|
|
103
|
+
promise.resolve(withResult: ())
|
|
104
|
+
}
|
|
105
|
+
return promise
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/// On the player queue: the anchor id is player-queue state.
|
|
109
|
+
func shufflePlaylist(playlistId: String, options: ShufflePlaylistOptions?) throws -> Promise<Void> {
|
|
110
|
+
let promise = Promise<Void>()
|
|
111
|
+
core.playerQueue.async {
|
|
112
|
+
let isCurrent = self.core.currentPlaylistId == playlistId
|
|
113
|
+
let pin: String? =
|
|
114
|
+
(isCurrent && options?.keepCurrentTrackFirst != false)
|
|
115
|
+
? self.core.currentTracks[safe: self.core.currentTrackIndex]?.id : nil
|
|
116
|
+
guard let changed = self.playlistManager.shufflePlaylist(playlistId: playlistId, firstTrackId: pin) else {
|
|
117
|
+
promise.reject(withError: RuntimeError.error(withMessage: "Playlist not found: \(playlistId)"))
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
if changed, isCurrent { self.core.syncCurrentPlaylistOnQueue(playlistId: playlistId) }
|
|
121
|
+
promise.resolve(withResult: ())
|
|
122
|
+
}
|
|
123
|
+
return promise
|
|
124
|
+
}
|
|
125
|
+
|
|
97
126
|
// MARK: - Playback control
|
|
98
127
|
|
|
99
128
|
/// Enqueued synchronously on the player queue so a `loadPlaylist()` followed by
|
|
@@ -130,13 +159,11 @@ final class HybridPlayerQueue: HybridPlayerQueueSpec {
|
|
|
130
159
|
}
|
|
131
160
|
|
|
132
161
|
func onPlaylistChanged(callback: @escaping (_ playlistId: String, _ playlist: Playlist, _ operation: QueueOperation?) -> Void) throws {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
callback(updated.id, updated.toGeneratedPlaylist(), operation)
|
|
137
|
-
}
|
|
138
|
-
playlistChangeRemovers.append(remover)
|
|
162
|
+
// Manager-level listener: covers playlists created after registration.
|
|
163
|
+
let remover = playlistManager.addAnyPlaylistChangeListener { updated, operation in
|
|
164
|
+
callback(updated.id, updated.toGeneratedPlaylist(), operation)
|
|
139
165
|
}
|
|
166
|
+
playlistChangeRemovers.append(remover)
|
|
140
167
|
}
|
|
141
168
|
|
|
142
169
|
// MARK: - Cleanup
|
|
@@ -21,6 +21,7 @@ declare class CallbackSubscriptionManager {
|
|
|
21
21
|
private playlistsChangedSubscribers;
|
|
22
22
|
private androidAutoConnectionSubscribers;
|
|
23
23
|
private isPlaylistsChangedRegistered;
|
|
24
|
+
private playlistsChangedFanOutScheduled;
|
|
24
25
|
private isAndroidAutoConnectionRegistered;
|
|
25
26
|
private isPlaybackStateRegistered;
|
|
26
27
|
private isTrackChangeRegistered;
|
|
@@ -61,6 +62,7 @@ declare class CallbackSubscriptionManager {
|
|
|
61
62
|
* @returns Unsubscribe function
|
|
62
63
|
*/
|
|
63
64
|
subscribeToPlaylistsChanged(callback: () => void): () => void;
|
|
65
|
+
private schedulePlaylistsChangedFanOut;
|
|
64
66
|
private ensurePlaylistsChangedRegistered;
|
|
65
67
|
private ensureTemporaryQueueRegistered;
|
|
66
68
|
private ensureSeekRegistered;
|
|
@@ -14,6 +14,7 @@ class CallbackSubscriptionManager {
|
|
|
14
14
|
playlistsChangedSubscribers = new Set();
|
|
15
15
|
androidAutoConnectionSubscribers = new Set();
|
|
16
16
|
isPlaylistsChangedRegistered = false;
|
|
17
|
+
playlistsChangedFanOutScheduled = false;
|
|
17
18
|
isAndroidAutoConnectionRegistered = false;
|
|
18
19
|
isPlaybackStateRegistered = false;
|
|
19
20
|
isTrackChangeRegistered = false;
|
|
@@ -147,19 +148,33 @@ class CallbackSubscriptionManager {
|
|
|
147
148
|
this.playlistsChangedSubscribers.delete(callback);
|
|
148
149
|
};
|
|
149
150
|
}
|
|
151
|
+
// Task-level, not a microtask: native callbacks arrive as separate JS tasks.
|
|
152
|
+
schedulePlaylistsChangedFanOut() {
|
|
153
|
+
if (this.playlistsChangedFanOutScheduled)
|
|
154
|
+
return;
|
|
155
|
+
this.playlistsChangedFanOutScheduled = true;
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
this.playlistsChangedFanOutScheduled = false;
|
|
158
|
+
this.playlistsChangedSubscribers.forEach((subscriber) => {
|
|
159
|
+
try {
|
|
160
|
+
subscriber();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
console.error('[CallbackManager] Error in playlists changed subscriber:', error);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}, 0);
|
|
167
|
+
}
|
|
150
168
|
ensurePlaylistsChangedRegistered() {
|
|
151
169
|
if (this.isPlaylistsChangedRegistered)
|
|
152
170
|
return;
|
|
153
171
|
try {
|
|
154
172
|
PlayerQueue.onPlaylistsChanged(() => {
|
|
155
|
-
this.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
console.error('[CallbackManager] Error in playlists changed subscriber:', error);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
173
|
+
this.schedulePlaylistsChangedFanOut();
|
|
174
|
+
});
|
|
175
|
+
// Track-level mutations only fire the per-playlist event.
|
|
176
|
+
PlayerQueue.onPlaylistChanged(() => {
|
|
177
|
+
this.schedulePlaylistsChangedFanOut();
|
|
163
178
|
});
|
|
164
179
|
this.isPlaylistsChangedRegistered = true;
|
|
165
180
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// TODO: Export all HybridObjects here for the user
|
|
2
2
|
import { NitroModules } from 'react-native-nitro-modules';
|
|
3
|
-
import { Platform } from 'react-native';
|
|
3
|
+
import { AppRegistry, Platform } from 'react-native';
|
|
4
|
+
// Native runs this while it needs JS without a resumed activity (car, screen off): it keeps JS timers ticking
|
|
5
|
+
if (Platform.OS === 'android') {
|
|
6
|
+
AppRegistry.registerHeadlessTask('NitroPlayerKeepAlive', () => () => new Promise(() => { }));
|
|
7
|
+
}
|
|
4
8
|
export const PlayerQueue = NitroModules.createHybridObject('PlayerQueue');
|
|
5
9
|
export const TrackPlayer = NitroModules.createHybridObject('TrackPlayer');
|
|
6
10
|
// Android-only: Android Auto Media Library
|
|
@@ -18,4 +18,16 @@ export interface AndroidAutoMediaLibrary extends HybridObject<{
|
|
|
18
18
|
* Falls back to showing all playlists
|
|
19
19
|
*/
|
|
20
20
|
clearMediaLibrary(): Promise<void>;
|
|
21
|
+
/** Android Auto opened a folder published with `children: []`; answer with {@link resolveRequest}. */
|
|
22
|
+
onLoadChildren(callback: (requestId: string, parentId: string) => void): void;
|
|
23
|
+
/** A car search, or a voice request the loaded playlists cannot match; answer with {@link resolveRequest}. */
|
|
24
|
+
onSearch(callback: (requestId: string, query: string) => void): void;
|
|
25
|
+
/** Answers {@link onLoadChildren} / {@link onSearch} with a JSON array of MediaItem. */
|
|
26
|
+
resolveRequest(requestId: string, itemsJson: string): void;
|
|
27
|
+
/** Replaces the session buttons with a JSON array of SessionButton. */
|
|
28
|
+
setSessionButtons(buttonsJson: string): void;
|
|
29
|
+
/** Called with the button's `action` when a session button is pressed. */
|
|
30
|
+
onSessionButtonPress(callback: (action: string) => void): void;
|
|
31
|
+
/** A controller asked to play with nothing loaded (car auto-play, play on connect); load the last queue now. */
|
|
32
|
+
onPlaybackResumption(callback: () => void): void;
|
|
21
33
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
-
import type { QueueOperation, Reason, TrackItem, TrackPlayerState, PlayerState, PlayerConfig, Playlist, TimedMetadata } from '../types/PlayerQueue';
|
|
2
|
+
import type { QueueOperation, Reason, TrackItem, TrackPlayerState, PlayerState, PlayerConfig, Playlist, TimedMetadata, ShufflePlaylistOptions } from '../types/PlayerQueue';
|
|
3
3
|
export interface PlayerQueue extends HybridObject<{
|
|
4
4
|
android: 'kotlin';
|
|
5
5
|
ios: 'swift';
|
|
@@ -13,6 +13,10 @@ export interface PlayerQueue extends HybridObject<{
|
|
|
13
13
|
addTracksToPlaylist(playlistId: string, tracks: TrackItem[], index?: number): Promise<void>;
|
|
14
14
|
removeTrackFromPlaylist(playlistId: string, trackId: string): Promise<void>;
|
|
15
15
|
reorderTrackInPlaylist(playlistId: string, trackId: string, newIndex: number): Promise<void>;
|
|
16
|
+
/** One native pass. Unknown ids are skipped; rejects only if the playlist does not exist. */
|
|
17
|
+
removeTracksFromPlaylist(playlistId: string, trackIds: string[]): Promise<void>;
|
|
18
|
+
/** Fisher-Yates shuffle of the stored playlist, persisted. Never interrupts the playing item. */
|
|
19
|
+
shufflePlaylist(playlistId: string, options?: ShufflePlaylistOptions): Promise<void>;
|
|
16
20
|
loadPlaylist(playlistId: string, index?: number): Promise<void>;
|
|
17
21
|
getCurrentPlaylistId(): string | null;
|
|
18
22
|
onPlaylistsChanged(callback: (playlists: Playlist[], operation?: QueueOperation) => void): void;
|
|
@@ -28,6 +28,8 @@ export interface MediaItem {
|
|
|
28
28
|
children?: MediaItem[];
|
|
29
29
|
/** Layout type for folder items (overrides library default) */
|
|
30
30
|
layoutType?: LayoutType;
|
|
31
|
+
/** Group header; contiguous items sharing one are listed under it */
|
|
32
|
+
groupTitle?: string;
|
|
31
33
|
}
|
|
32
34
|
/**
|
|
33
35
|
* Media library structure for Android Auto
|
|
@@ -42,3 +44,12 @@ export interface MediaLibrary {
|
|
|
42
44
|
/** Optional app icon URL */
|
|
43
45
|
appIconUrl?: string;
|
|
44
46
|
}
|
|
47
|
+
export type SessionButtonIcon = 'heart' | 'heart_filled' | 'star' | 'star_filled' | 'thumb_up' | 'thumb_up_filled' | 'plus' | 'check';
|
|
48
|
+
/** App-defined button beside the transport controls in Android Auto and the notification */
|
|
49
|
+
export interface SessionButton {
|
|
50
|
+
/** Passed to the press callback */
|
|
51
|
+
action: string;
|
|
52
|
+
/** Accessibility label */
|
|
53
|
+
title: string;
|
|
54
|
+
icon: SessionButtonIcon;
|
|
55
|
+
}
|
|
@@ -18,6 +18,10 @@ export interface Playlist {
|
|
|
18
18
|
tracks: TrackItem[];
|
|
19
19
|
}
|
|
20
20
|
export type QueueOperation = 'add' | 'remove' | 'clear' | 'update';
|
|
21
|
+
export interface ShufflePlaylistOptions {
|
|
22
|
+
/** Move the playing track to index 0 and shuffle the rest behind it (default: true). */
|
|
23
|
+
keepCurrentTrackFirst?: boolean;
|
|
24
|
+
}
|
|
21
25
|
export type TrackPlayerState = 'playing' | 'paused' | 'stopped' | 'buffering';
|
|
22
26
|
export type Reason = 'user_action' | 'skip' | 'end' | 'error' | 'repeat';
|
|
23
27
|
export interface PlayerState {
|
|
@@ -44,6 +48,14 @@ export interface TimedMetadata {
|
|
|
44
48
|
/** Artwork URL the stream tagged as artwork, when it sends one explicitly */
|
|
45
49
|
artworkUrl?: string;
|
|
46
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* How Android reacts when another app takes audio focus.
|
|
53
|
+
* - 'pause': pause for any interruption and resume after it (default, today's behaviour)
|
|
54
|
+
* - 'duck': keep playing at a reduced volume through transient interruptions
|
|
55
|
+
* - 'ignore': never react to audio focus at all
|
|
56
|
+
* iOS ignores this option.
|
|
57
|
+
*/
|
|
58
|
+
export type AndroidAudioFocusMode = 'pause' | 'duck' | 'ignore';
|
|
47
59
|
export interface PlayerConfig {
|
|
48
60
|
androidAutoEnabled?: boolean;
|
|
49
61
|
carPlayEnabled?: boolean;
|
|
@@ -62,4 +74,6 @@ export interface PlayerConfig {
|
|
|
62
74
|
*/
|
|
63
75
|
lookaheadCount?: number;
|
|
64
76
|
androidNotificationIcon?: string;
|
|
77
|
+
/** How Android reacts when another app takes audio focus (default: 'pause'). */
|
|
78
|
+
androidAudioFocus?: AndroidAudioFocusMode;
|
|
65
79
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { MediaLibrary } from '../types/AndroidAutoMediaLibrary';
|
|
1
|
+
import type { MediaItem, MediaLibrary, SessionButton } from '../types/AndroidAutoMediaLibrary';
|
|
2
|
+
type ItemsProvider = (argument: string) => Promise<MediaItem[] | null | undefined>;
|
|
2
3
|
/**
|
|
3
4
|
* Helper utilities for Android Auto Media Library
|
|
4
5
|
* Android-only functionality
|
|
@@ -40,8 +41,19 @@ export declare const AndroidAutoMediaLibraryHelper: {
|
|
|
40
41
|
* Falls back to showing all playlists
|
|
41
42
|
*/
|
|
42
43
|
clear: () => void;
|
|
44
|
+
/** Children of folders published with `children: []`; playable ids are `${playlistId}:${trackId}`, empty after 10s. Register at module scope: the car can start the app without UI. */
|
|
45
|
+
setChildrenLoader: (loader: ItemsProvider) => void;
|
|
46
|
+
/** Answers car searches and unmatched voice requests (first playable result plays); search shows from the next connection. */
|
|
47
|
+
setSearchHandler: (handler: ItemsProvider) => void;
|
|
48
|
+
/** Buttons beside the transport controls in Android Auto and the notification; call again to change their state. */
|
|
49
|
+
setSessionButtons: (buttons: SessionButton[]) => void;
|
|
50
|
+
/** Called with the pressed button's `action`. */
|
|
51
|
+
onSessionButtonPress: (callback: (action: string) => void) => void;
|
|
52
|
+
/** Car auto-play or play-on-connect found nothing loaded: restore your last queue (playback starts once it is loaded). */
|
|
53
|
+
onPlaybackResumption: (callback: () => void) => void;
|
|
43
54
|
/**
|
|
44
55
|
* Check if Android Auto Media Library is available (Android only)
|
|
45
56
|
*/
|
|
46
57
|
isAvailable: () => boolean;
|
|
47
58
|
};
|
|
59
|
+
export {};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { AndroidAutoMediaLibrary as AndroidAutoMediaLibraryModule } from '../index';
|
|
2
|
+
const answer = (requestId, provide) => provide()
|
|
3
|
+
.catch(() => null)
|
|
4
|
+
.then((items) => AndroidAutoMediaLibraryModule?.resolveRequest(requestId, JSON.stringify(items ?? [])));
|
|
2
5
|
/**
|
|
3
6
|
* Helper utilities for Android Auto Media Library
|
|
4
7
|
* Android-only functionality
|
|
@@ -53,6 +56,26 @@ export const AndroidAutoMediaLibraryHelper = {
|
|
|
53
56
|
}
|
|
54
57
|
AndroidAutoMediaLibraryModule.clearMediaLibrary();
|
|
55
58
|
},
|
|
59
|
+
/** Children of folders published with `children: []`; playable ids are `${playlistId}:${trackId}`, empty after 10s. Register at module scope: the car can start the app without UI. */
|
|
60
|
+
setChildrenLoader: (loader) => {
|
|
61
|
+
AndroidAutoMediaLibraryModule?.onLoadChildren((requestId, parentId) => answer(requestId, () => loader(parentId)));
|
|
62
|
+
},
|
|
63
|
+
/** Answers car searches and unmatched voice requests (first playable result plays); search shows from the next connection. */
|
|
64
|
+
setSearchHandler: (handler) => {
|
|
65
|
+
AndroidAutoMediaLibraryModule?.onSearch((requestId, query) => answer(requestId, () => handler(query)));
|
|
66
|
+
},
|
|
67
|
+
/** Buttons beside the transport controls in Android Auto and the notification; call again to change their state. */
|
|
68
|
+
setSessionButtons: (buttons) => {
|
|
69
|
+
AndroidAutoMediaLibraryModule?.setSessionButtons(JSON.stringify(buttons));
|
|
70
|
+
},
|
|
71
|
+
/** Called with the pressed button's `action`. */
|
|
72
|
+
onSessionButtonPress: (callback) => {
|
|
73
|
+
AndroidAutoMediaLibraryModule?.onSessionButtonPress(callback);
|
|
74
|
+
},
|
|
75
|
+
/** Car auto-play or play-on-connect found nothing loaded: restore your last queue (playback starts once it is loaded). */
|
|
76
|
+
onPlaybackResumption: (callback) => {
|
|
77
|
+
AndroidAutoMediaLibraryModule?.onPlaybackResumption(callback);
|
|
78
|
+
},
|
|
56
79
|
/**
|
|
57
80
|
* Check if Android Auto Media Library is available (Android only)
|
|
58
81
|
*/
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
#include <NitroModules/HybridObjectRegistry.hpp>
|
|
17
17
|
|
|
18
18
|
#include "JHybridAndroidAutoMediaLibrarySpec.hpp"
|
|
19
|
+
#include "JFunc_void_std__string_std__string.hpp"
|
|
20
|
+
#include "JFunc_void_std__string.hpp"
|
|
21
|
+
#include "JFunc_void.hpp"
|
|
19
22
|
#include "JHybridAudioDevicesSpec.hpp"
|
|
20
23
|
#include "JHybridCastSpec.hpp"
|
|
21
24
|
#include "JFunc_void_CastState_std__optional_std__variant_nitro__NullType__std__string__.hpp"
|
|
@@ -112,6 +115,9 @@ void registerAllNatives() {
|
|
|
112
115
|
|
|
113
116
|
// Register native JNI methods
|
|
114
117
|
margelo::nitro::nitroplayer::JHybridAndroidAutoMediaLibrarySpec::CxxPart::registerNatives();
|
|
118
|
+
margelo::nitro::nitroplayer::JFunc_void_std__string_std__string_cxx::registerNatives();
|
|
119
|
+
margelo::nitro::nitroplayer::JFunc_void_std__string_cxx::registerNatives();
|
|
120
|
+
margelo::nitro::nitroplayer::JFunc_void_cxx::registerNatives();
|
|
115
121
|
margelo::nitro::nitroplayer::JHybridAudioDevicesSpec::CxxPart::registerNatives();
|
|
116
122
|
margelo::nitro::nitroplayer::JHybridCastSpec::CxxPart::registerNatives();
|
|
117
123
|
margelo::nitro::nitroplayer::JFunc_void_CastState_std__optional_std__variant_nitro__NullType__std__string___cxx::registerNatives();
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// JAndroidAudioFocusMode.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <fbjni/fbjni.h>
|
|
11
|
+
#include "AndroidAudioFocusMode.hpp"
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro::nitroplayer {
|
|
14
|
+
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The C++ JNI bridge between the C++ enum "AndroidAudioFocusMode" and the the Kotlin enum "AndroidAudioFocusMode".
|
|
19
|
+
*/
|
|
20
|
+
struct JAndroidAudioFocusMode final: public jni::JavaClass<JAndroidAudioFocusMode> {
|
|
21
|
+
public:
|
|
22
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitroplayer/AndroidAudioFocusMode;";
|
|
23
|
+
|
|
24
|
+
public:
|
|
25
|
+
/**
|
|
26
|
+
* Convert this Java/Kotlin-based enum to the C++ enum AndroidAudioFocusMode.
|
|
27
|
+
*/
|
|
28
|
+
[[maybe_unused]]
|
|
29
|
+
[[nodiscard]]
|
|
30
|
+
AndroidAudioFocusMode toCpp() const {
|
|
31
|
+
static const auto clazz = javaClassStatic();
|
|
32
|
+
static const auto fieldOrdinal = clazz->getField<int>("value");
|
|
33
|
+
int ordinal = this->getFieldValue(fieldOrdinal);
|
|
34
|
+
return static_cast<AndroidAudioFocusMode>(ordinal);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public:
|
|
38
|
+
/**
|
|
39
|
+
* Create a Java/Kotlin-based enum with the given C++ enum's value.
|
|
40
|
+
*/
|
|
41
|
+
[[maybe_unused]]
|
|
42
|
+
static jni::alias_ref<JAndroidAudioFocusMode> fromCpp(AndroidAudioFocusMode value) {
|
|
43
|
+
static const auto clazz = javaClassStatic();
|
|
44
|
+
switch (value) {
|
|
45
|
+
case AndroidAudioFocusMode::PAUSE:
|
|
46
|
+
static const auto fieldPAUSE = clazz->getStaticField<JAndroidAudioFocusMode>("PAUSE");
|
|
47
|
+
return clazz->getStaticFieldValue(fieldPAUSE);
|
|
48
|
+
case AndroidAudioFocusMode::DUCK:
|
|
49
|
+
static const auto fieldDUCK = clazz->getStaticField<JAndroidAudioFocusMode>("DUCK");
|
|
50
|
+
return clazz->getStaticFieldValue(fieldDUCK);
|
|
51
|
+
case AndroidAudioFocusMode::IGNORE:
|
|
52
|
+
static const auto fieldIGNORE = clazz->getStaticField<JAndroidAudioFocusMode>("IGNORE");
|
|
53
|
+
return clazz->getStaticFieldValue(fieldIGNORE);
|
|
54
|
+
default:
|
|
55
|
+
std::string stringValue = std::to_string(static_cast<int>(value));
|
|
56
|
+
throw std::invalid_argument("Invalid enum value (" + stringValue + "!");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
} // namespace margelo::nitro::nitroplayer
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// JFunc_void.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <fbjni/fbjni.h>
|
|
11
|
+
#include <functional>
|
|
12
|
+
|
|
13
|
+
#include <functional>
|
|
14
|
+
#include <NitroModules/JNICallable.hpp>
|
|
15
|
+
|
|
16
|
+
namespace margelo::nitro::nitroplayer {
|
|
17
|
+
|
|
18
|
+
using namespace facebook;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Represents the Java/Kotlin callback `() -> Unit`.
|
|
22
|
+
* This can be passed around between C++ and Java/Kotlin.
|
|
23
|
+
*/
|
|
24
|
+
struct JFunc_void: public jni::JavaClass<JFunc_void> {
|
|
25
|
+
public:
|
|
26
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitroplayer/Func_void;";
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
/**
|
|
30
|
+
* Invokes the function this `JFunc_void` instance holds through JNI.
|
|
31
|
+
*/
|
|
32
|
+
void invoke() const {
|
|
33
|
+
static const auto method = javaClassStatic()->getMethod<void()>("invoke");
|
|
34
|
+
method(self());
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An implementation of Func_void that is backed by a C++ implementation (using `std::function<...>`)
|
|
40
|
+
*/
|
|
41
|
+
class JFunc_void_cxx final: public jni::HybridClass<JFunc_void_cxx, JFunc_void> {
|
|
42
|
+
public:
|
|
43
|
+
static jni::local_ref<JFunc_void::javaobject> fromCpp(const std::function<void()>& func) {
|
|
44
|
+
return JFunc_void_cxx::newObjectCxxArgs(func);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public:
|
|
48
|
+
/**
|
|
49
|
+
* Invokes the C++ `std::function<...>` this `JFunc_void_cxx` instance holds.
|
|
50
|
+
*/
|
|
51
|
+
void invoke_cxx() {
|
|
52
|
+
_func();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public:
|
|
56
|
+
[[nodiscard]]
|
|
57
|
+
inline const std::function<void()>& getFunction() const {
|
|
58
|
+
return _func;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public:
|
|
62
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitroplayer/Func_void_cxx;";
|
|
63
|
+
static void registerNatives() {
|
|
64
|
+
registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_cxx::invoke_cxx)});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private:
|
|
68
|
+
explicit JFunc_void_cxx(const std::function<void()>& func): _func(func) { }
|
|
69
|
+
|
|
70
|
+
private:
|
|
71
|
+
friend HybridBase;
|
|
72
|
+
std::function<void()> _func;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
} // namespace margelo::nitro::nitroplayer
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// JFunc_void_std__string.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <fbjni/fbjni.h>
|
|
11
|
+
#include <functional>
|
|
12
|
+
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <functional>
|
|
15
|
+
#include <NitroModules/JNICallable.hpp>
|
|
16
|
+
|
|
17
|
+
namespace margelo::nitro::nitroplayer {
|
|
18
|
+
|
|
19
|
+
using namespace facebook;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Represents the Java/Kotlin callback `(action: String) -> Unit`.
|
|
23
|
+
* This can be passed around between C++ and Java/Kotlin.
|
|
24
|
+
*/
|
|
25
|
+
struct JFunc_void_std__string: public jni::JavaClass<JFunc_void_std__string> {
|
|
26
|
+
public:
|
|
27
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitroplayer/Func_void_std__string;";
|
|
28
|
+
|
|
29
|
+
public:
|
|
30
|
+
/**
|
|
31
|
+
* Invokes the function this `JFunc_void_std__string` instance holds through JNI.
|
|
32
|
+
*/
|
|
33
|
+
void invoke(const std::string& action) const {
|
|
34
|
+
static const auto method = javaClassStatic()->getMethod<void(jni::alias_ref<jni::JString> /* action */)>("invoke");
|
|
35
|
+
method(self(), jni::make_jstring(action));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* An implementation of Func_void_std__string that is backed by a C++ implementation (using `std::function<...>`)
|
|
41
|
+
*/
|
|
42
|
+
class JFunc_void_std__string_cxx final: public jni::HybridClass<JFunc_void_std__string_cxx, JFunc_void_std__string> {
|
|
43
|
+
public:
|
|
44
|
+
static jni::local_ref<JFunc_void_std__string::javaobject> fromCpp(const std::function<void(const std::string& /* action */)>& func) {
|
|
45
|
+
return JFunc_void_std__string_cxx::newObjectCxxArgs(func);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public:
|
|
49
|
+
/**
|
|
50
|
+
* Invokes the C++ `std::function<...>` this `JFunc_void_std__string_cxx` instance holds.
|
|
51
|
+
*/
|
|
52
|
+
void invoke_cxx(jni::alias_ref<jni::JString> action) {
|
|
53
|
+
_func(action->toStdString());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public:
|
|
57
|
+
[[nodiscard]]
|
|
58
|
+
inline const std::function<void(const std::string& /* action */)>& getFunction() const {
|
|
59
|
+
return _func;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public:
|
|
63
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitroplayer/Func_void_std__string_cxx;";
|
|
64
|
+
static void registerNatives() {
|
|
65
|
+
registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_std__string_cxx::invoke_cxx)});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private:
|
|
69
|
+
explicit JFunc_void_std__string_cxx(const std::function<void(const std::string& /* action */)>& func): _func(func) { }
|
|
70
|
+
|
|
71
|
+
private:
|
|
72
|
+
friend HybridBase;
|
|
73
|
+
std::function<void(const std::string& /* action */)> _func;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
} // namespace margelo::nitro::nitroplayer
|