react-native-nitro-player 1.7.0-alpha.3 → 1.7.0-alpha.4

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 (59) hide show
  1. package/README.md +9 -0
  2. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +45 -7
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAudioFocus.kt +65 -0
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +46 -14
  5. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +2 -0
  6. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +36 -12
  7. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +1 -1
  8. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +9 -3
  9. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +7 -3
  10. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/AudioFocusController.kt +87 -0
  11. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/ExoPlayerBuilder.kt +3 -0
  12. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +8 -2
  13. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +26 -1
  14. package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +60 -0
  15. package/ios/cast/CastQueueDiff.swift +58 -0
  16. package/ios/cast/CastQueueReconciler.swift +228 -0
  17. package/ios/core/CastSessionManager.swift +215 -31
  18. package/ios/core/TrackPlayerCast.swift +96 -65
  19. package/ios/core/TrackPlayerCore.swift +31 -0
  20. package/ios/core/TrackPlayerListener.swift +19 -3
  21. package/ios/core/TrackPlayerPlayback.swift +12 -10
  22. package/ios/core/TrackPlayerQueue.swift +10 -2
  23. package/ios/core/TrackPlayerQueueBuild.swift +21 -9
  24. package/ios/core/TrackPlayerTempQueue.swift +27 -15
  25. package/ios/core/TrackPlayerUrlLoader.swift +6 -2
  26. package/ios/playlist/PlaylistManager.swift +60 -1
  27. package/ios/queue/HybridPlayerQueue.swift +40 -13
  28. package/lib/hooks/callbackManager.d.ts +2 -0
  29. package/lib/hooks/callbackManager.js +23 -8
  30. package/lib/specs/TrackPlayer.nitro.d.ts +5 -1
  31. package/lib/types/PlayerQueue.d.ts +14 -0
  32. package/nitrogen/generated/android/c++/JAndroidAudioFocusMode.hpp +61 -0
  33. package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.cpp +43 -0
  34. package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.hpp +2 -0
  35. package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +4 -0
  36. package/nitrogen/generated/android/c++/JPlayerConfig.hpp +9 -3
  37. package/nitrogen/generated/android/c++/JShufflePlaylistOptions.hpp +57 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/AndroidAudioFocusMode.kt +24 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridPlayerQueueSpec.kt +8 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +6 -3
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/ShufflePlaylistOptions.kt +38 -0
  42. package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +36 -0
  43. package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Umbrella.hpp +6 -0
  44. package/nitrogen/generated/ios/c++/HybridPlayerQueueSpecSwift.hpp +19 -0
  45. package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +3 -0
  46. package/nitrogen/generated/ios/swift/AndroidAudioFocusMode.swift +44 -0
  47. package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec.swift +2 -0
  48. package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec_cxx.swift +38 -0
  49. package/nitrogen/generated/ios/swift/PlayerConfig.swift +12 -1
  50. package/nitrogen/generated/ios/swift/ShufflePlaylistOptions.swift +42 -0
  51. package/nitrogen/generated/shared/c++/AndroidAudioFocusMode.hpp +80 -0
  52. package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.cpp +2 -0
  53. package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.hpp +5 -0
  54. package/nitrogen/generated/shared/c++/PlayerConfig.hpp +9 -3
  55. package/nitrogen/generated/shared/c++/ShufflePlaylistOptions.hpp +83 -0
  56. package/package.json +1 -1
  57. package/src/hooks/callbackManager.ts +25 -10
  58. package/src/specs/TrackPlayer.nitro.ts +11 -0
  59. package/src/types/PlayerQueue.ts +17 -0
@@ -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
- Promise.async {
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
- let allPlaylists = playlistManager.getAllPlaylists()
134
- for playlist in allPlaylists {
135
- let remover = playlistManager.addPlaylistChangeListener(playlistId: playlist.id) { updated, operation in
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.playlistsChangedSubscribers.forEach((subscriber) => {
156
- try {
157
- subscriber();
158
- }
159
- catch (error) {
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
  }
@@ -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;
@@ -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
  }
@@ -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
@@ -11,6 +11,8 @@
11
11
  namespace margelo::nitro::nitroplayer { struct Playlist; }
12
12
  // Forward declaration of `TrackItem` to properly resolve imports.
13
13
  namespace margelo::nitro::nitroplayer { struct TrackItem; }
14
+ // Forward declaration of `ShufflePlaylistOptions` to properly resolve imports.
15
+ namespace margelo::nitro::nitroplayer { struct ShufflePlaylistOptions; }
14
16
  // Forward declaration of `QueueOperation` to properly resolve imports.
15
17
  namespace margelo::nitro::nitroplayer { enum class QueueOperation; }
16
18
 
@@ -31,6 +33,8 @@ namespace margelo::nitro::nitroplayer { enum class QueueOperation; }
31
33
  #include "JTrackItem.hpp"
32
34
  #include <NitroModules/AnyMap.hpp>
33
35
  #include <NitroModules/JAnyMap.hpp>
36
+ #include "ShufflePlaylistOptions.hpp"
37
+ #include "JShufflePlaylistOptions.hpp"
34
38
  #include "QueueOperation.hpp"
35
39
  #include <functional>
36
40
  #include "JFunc_void_std__vector_Playlist__std__optional_QueueOperation_.hpp"
@@ -205,6 +209,45 @@ namespace margelo::nitro::nitroplayer {
205
209
  return __promise;
206
210
  }();
207
211
  }
212
+ std::shared_ptr<Promise<void>> JHybridPlayerQueueSpec::removeTracksFromPlaylist(const std::string& playlistId, const std::vector<std::string>& trackIds) {
213
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* playlistId */, jni::alias_ref<jni::JArrayClass<jni::JString>> /* trackIds */)>("removeTracksFromPlaylist");
214
+ auto __result = method(_javaPart, jni::make_jstring(playlistId), [&]() {
215
+ size_t __size = trackIds.size();
216
+ jni::local_ref<jni::JArrayClass<jni::JString>> __array = jni::JArrayClass<jni::JString>::newArray(__size);
217
+ for (size_t __i = 0; __i < __size; __i++) {
218
+ const auto& __element = trackIds[__i];
219
+ auto __elementJni = jni::make_jstring(__element);
220
+ __array->setElement(__i, *__elementJni);
221
+ }
222
+ return __array;
223
+ }());
224
+ return [&]() {
225
+ auto __promise = Promise<void>::create();
226
+ __result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
227
+ __promise->resolve();
228
+ });
229
+ __result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
230
+ jni::JniException __jniError(__throwable);
231
+ __promise->reject(std::make_exception_ptr(__jniError));
232
+ });
233
+ return __promise;
234
+ }();
235
+ }
236
+ std::shared_ptr<Promise<void>> JHybridPlayerQueueSpec::shufflePlaylist(const std::string& playlistId, const std::optional<ShufflePlaylistOptions>& options) {
237
+ static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* playlistId */, jni::alias_ref<JShufflePlaylistOptions> /* options */)>("shufflePlaylist");
238
+ auto __result = method(_javaPart, jni::make_jstring(playlistId), options.has_value() ? JShufflePlaylistOptions::fromCpp(options.value()) : nullptr);
239
+ return [&]() {
240
+ auto __promise = Promise<void>::create();
241
+ __result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
242
+ __promise->resolve();
243
+ });
244
+ __result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
245
+ jni::JniException __jniError(__throwable);
246
+ __promise->reject(std::make_exception_ptr(__jniError));
247
+ });
248
+ return __promise;
249
+ }();
250
+ }
208
251
  std::shared_ptr<Promise<void>> JHybridPlayerQueueSpec::loadPlaylist(const std::string& playlistId, std::optional<double> index) {
209
252
  static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* playlistId */, jni::alias_ref<jni::JDouble> /* index */)>("loadPlaylist");
210
253
  auto __result = method(_javaPart, jni::make_jstring(playlistId), index.has_value() ? jni::JDouble::valueOf(index.value()) : nullptr);
@@ -63,6 +63,8 @@ namespace margelo::nitro::nitroplayer {
63
63
  std::shared_ptr<Promise<void>> addTracksToPlaylist(const std::string& playlistId, const std::vector<TrackItem>& tracks, std::optional<double> index) override;
64
64
  std::shared_ptr<Promise<void>> removeTrackFromPlaylist(const std::string& playlistId, const std::string& trackId) override;
65
65
  std::shared_ptr<Promise<void>> reorderTrackInPlaylist(const std::string& playlistId, const std::string& trackId, double newIndex) override;
66
+ std::shared_ptr<Promise<void>> removeTracksFromPlaylist(const std::string& playlistId, const std::vector<std::string>& trackIds) override;
67
+ std::shared_ptr<Promise<void>> shufflePlaylist(const std::string& playlistId, const std::optional<ShufflePlaylistOptions>& options) override;
66
68
  std::shared_ptr<Promise<void>> loadPlaylist(const std::string& playlistId, std::optional<double> index) override;
67
69
  std::variant<nitro::NullType, std::string> getCurrentPlaylistId() override;
68
70
  void onPlaylistsChanged(const std::function<void(const std::vector<Playlist>& /* playlists */, std::optional<QueueOperation> /* operation */)>& callback) override;
@@ -19,6 +19,8 @@ namespace margelo::nitro::nitroplayer { enum class CurrentPlayingType; }
19
19
  namespace margelo::nitro::nitroplayer { enum class RepeatMode; }
20
20
  // Forward declaration of `PlayerConfig` to properly resolve imports.
21
21
  namespace margelo::nitro::nitroplayer { struct PlayerConfig; }
22
+ // Forward declaration of `AndroidAudioFocusMode` to properly resolve imports.
23
+ namespace margelo::nitro::nitroplayer { enum class AndroidAudioFocusMode; }
22
24
  // Forward declaration of `Reason` to properly resolve imports.
23
25
  namespace margelo::nitro::nitroplayer { enum class Reason; }
24
26
  // Forward declaration of `TimedMetadata` to properly resolve imports.
@@ -49,6 +51,8 @@ namespace margelo::nitro::nitroplayer { struct TimedMetadata; }
49
51
  #include "JRepeatMode.hpp"
50
52
  #include "PlayerConfig.hpp"
51
53
  #include "JPlayerConfig.hpp"
54
+ #include "AndroidAudioFocusMode.hpp"
55
+ #include "JAndroidAudioFocusMode.hpp"
52
56
  #include "Reason.hpp"
53
57
  #include <functional>
54
58
  #include "JFunc_void_TrackItem_std__optional_Reason_.hpp"
@@ -10,6 +10,8 @@
10
10
  #include <fbjni/fbjni.h>
11
11
  #include "PlayerConfig.hpp"
12
12
 
13
+ #include "AndroidAudioFocusMode.hpp"
14
+ #include "JAndroidAudioFocusMode.hpp"
13
15
  #include <optional>
14
16
  #include <string>
15
17
 
@@ -46,6 +48,8 @@ namespace margelo::nitro::nitroplayer {
46
48
  jni::local_ref<jni::JDouble> lookaheadCount = this->getFieldValue(fieldLookaheadCount);
47
49
  static const auto fieldAndroidNotificationIcon = clazz->getField<jni::JString>("androidNotificationIcon");
48
50
  jni::local_ref<jni::JString> androidNotificationIcon = this->getFieldValue(fieldAndroidNotificationIcon);
51
+ static const auto fieldAndroidAudioFocus = clazz->getField<JAndroidAudioFocusMode>("androidAudioFocus");
52
+ jni::local_ref<JAndroidAudioFocusMode> androidAudioFocus = this->getFieldValue(fieldAndroidAudioFocus);
49
53
  return PlayerConfig(
50
54
  androidAutoEnabled != nullptr ? std::make_optional(static_cast<bool>(androidAutoEnabled->value())) : std::nullopt,
51
55
  carPlayEnabled != nullptr ? std::make_optional(static_cast<bool>(carPlayEnabled->value())) : std::nullopt,
@@ -53,7 +57,8 @@ namespace margelo::nitro::nitroplayer {
53
57
  remoteSkipForwardInterval != nullptr ? std::make_optional(remoteSkipForwardInterval->value()) : std::nullopt,
54
58
  remoteSkipBackwardInterval != nullptr ? std::make_optional(remoteSkipBackwardInterval->value()) : std::nullopt,
55
59
  lookaheadCount != nullptr ? std::make_optional(lookaheadCount->value()) : std::nullopt,
56
- androidNotificationIcon != nullptr ? std::make_optional(androidNotificationIcon->toStdString()) : std::nullopt
60
+ androidNotificationIcon != nullptr ? std::make_optional(androidNotificationIcon->toStdString()) : std::nullopt,
61
+ androidAudioFocus != nullptr ? std::make_optional(androidAudioFocus->toCpp()) : std::nullopt
57
62
  );
58
63
  }
59
64
 
@@ -63,7 +68,7 @@ namespace margelo::nitro::nitroplayer {
63
68
  */
64
69
  [[maybe_unused]]
65
70
  static jni::local_ref<JPlayerConfig::javaobject> fromCpp(const PlayerConfig& value) {
66
- using JSignature = JPlayerConfig(jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JString>);
71
+ using JSignature = JPlayerConfig(jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JDouble>, jni::alias_ref<jni::JString>, jni::alias_ref<JAndroidAudioFocusMode>);
67
72
  static const auto clazz = javaClassStatic();
68
73
  static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
69
74
  return create(
@@ -74,7 +79,8 @@ namespace margelo::nitro::nitroplayer {
74
79
  value.remoteSkipForwardInterval.has_value() ? jni::JDouble::valueOf(value.remoteSkipForwardInterval.value()) : nullptr,
75
80
  value.remoteSkipBackwardInterval.has_value() ? jni::JDouble::valueOf(value.remoteSkipBackwardInterval.value()) : nullptr,
76
81
  value.lookaheadCount.has_value() ? jni::JDouble::valueOf(value.lookaheadCount.value()) : nullptr,
77
- value.androidNotificationIcon.has_value() ? jni::make_jstring(value.androidNotificationIcon.value()) : nullptr
82
+ value.androidNotificationIcon.has_value() ? jni::make_jstring(value.androidNotificationIcon.value()) : nullptr,
83
+ value.androidAudioFocus.has_value() ? JAndroidAudioFocusMode::fromCpp(value.androidAudioFocus.value()) : nullptr
78
84
  );
79
85
  }
80
86
  };
@@ -0,0 +1,57 @@
1
+ ///
2
+ /// JShufflePlaylistOptions.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 "ShufflePlaylistOptions.hpp"
12
+
13
+ #include <optional>
14
+
15
+ namespace margelo::nitro::nitroplayer {
16
+
17
+ using namespace facebook;
18
+
19
+ /**
20
+ * The C++ JNI bridge between the C++ struct "ShufflePlaylistOptions" and the the Kotlin data class "ShufflePlaylistOptions".
21
+ */
22
+ struct JShufflePlaylistOptions final: public jni::JavaClass<JShufflePlaylistOptions> {
23
+ public:
24
+ static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/nitroplayer/ShufflePlaylistOptions;";
25
+
26
+ public:
27
+ /**
28
+ * Convert this Java/Kotlin-based struct to the C++ struct ShufflePlaylistOptions by copying all values to C++.
29
+ */
30
+ [[maybe_unused]]
31
+ [[nodiscard]]
32
+ ShufflePlaylistOptions toCpp() const {
33
+ static const auto clazz = javaClassStatic();
34
+ static const auto fieldKeepCurrentTrackFirst = clazz->getField<jni::JBoolean>("keepCurrentTrackFirst");
35
+ jni::local_ref<jni::JBoolean> keepCurrentTrackFirst = this->getFieldValue(fieldKeepCurrentTrackFirst);
36
+ return ShufflePlaylistOptions(
37
+ keepCurrentTrackFirst != nullptr ? std::make_optional(static_cast<bool>(keepCurrentTrackFirst->value())) : std::nullopt
38
+ );
39
+ }
40
+
41
+ public:
42
+ /**
43
+ * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java.
44
+ */
45
+ [[maybe_unused]]
46
+ static jni::local_ref<JShufflePlaylistOptions::javaobject> fromCpp(const ShufflePlaylistOptions& value) {
47
+ using JSignature = JShufflePlaylistOptions(jni::alias_ref<jni::JBoolean>);
48
+ static const auto clazz = javaClassStatic();
49
+ static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
50
+ return create(
51
+ clazz,
52
+ value.keepCurrentTrackFirst.has_value() ? jni::JBoolean::valueOf(value.keepCurrentTrackFirst.value()) : nullptr
53
+ );
54
+ }
55
+ };
56
+
57
+ } // namespace margelo::nitro::nitroplayer
@@ -0,0 +1,24 @@
1
+ ///
2
+ /// AndroidAudioFocusMode.kt
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
+ package com.margelo.nitro.nitroplayer
9
+
10
+ import androidx.annotation.Keep
11
+ import com.facebook.proguard.annotations.DoNotStrip
12
+
13
+ /**
14
+ * Represents the JavaScript enum/union "AndroidAudioFocusMode".
15
+ */
16
+ @DoNotStrip
17
+ @Keep
18
+ enum class AndroidAudioFocusMode(@DoNotStrip @Keep val value: Int) {
19
+ PAUSE(0),
20
+ DUCK(1),
21
+ IGNORE(2);
22
+
23
+ companion object
24
+ }
@@ -66,6 +66,14 @@ abstract class HybridPlayerQueueSpec: HybridObject() {
66
66
  @Keep
67
67
  abstract fun reorderTrackInPlaylist(playlistId: String, trackId: String, newIndex: Double): Promise<Unit>
68
68
 
69
+ @DoNotStrip
70
+ @Keep
71
+ abstract fun removeTracksFromPlaylist(playlistId: String, trackIds: Array<String>): Promise<Unit>
72
+
73
+ @DoNotStrip
74
+ @Keep
75
+ abstract fun shufflePlaylist(playlistId: String, options: ShufflePlaylistOptions?): Promise<Unit>
76
+
69
77
  @DoNotStrip
70
78
  @Keep
71
79
  abstract fun loadPlaylist(playlistId: String, index: Double?): Promise<Unit>
@@ -37,7 +37,10 @@ data class PlayerConfig(
37
37
  val lookaheadCount: Double?,
38
38
  @DoNotStrip
39
39
  @Keep
40
- val androidNotificationIcon: String?
40
+ val androidNotificationIcon: String?,
41
+ @DoNotStrip
42
+ @Keep
43
+ val androidAudioFocus: AndroidAudioFocusMode?
41
44
  ) {
42
45
  /* primary constructor */
43
46
 
@@ -49,8 +52,8 @@ data class PlayerConfig(
49
52
  @Keep
50
53
  @Suppress("unused")
51
54
  @JvmStatic
52
- private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, remoteSkipForwardInterval: Double?, remoteSkipBackwardInterval: Double?, lookaheadCount: Double?, androidNotificationIcon: String?): PlayerConfig {
53
- return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, remoteSkipForwardInterval, remoteSkipBackwardInterval, lookaheadCount, androidNotificationIcon)
55
+ private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, remoteSkipForwardInterval: Double?, remoteSkipBackwardInterval: Double?, lookaheadCount: Double?, androidNotificationIcon: String?, androidAudioFocus: AndroidAudioFocusMode?): PlayerConfig {
56
+ return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, remoteSkipForwardInterval, remoteSkipBackwardInterval, lookaheadCount, androidNotificationIcon, androidAudioFocus)
54
57
  }
55
58
  }
56
59
  }
@@ -0,0 +1,38 @@
1
+ ///
2
+ /// ShufflePlaylistOptions.kt
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
+ package com.margelo.nitro.nitroplayer
9
+
10
+ import androidx.annotation.Keep
11
+ import com.facebook.proguard.annotations.DoNotStrip
12
+
13
+
14
+ /**
15
+ * Represents the JavaScript object/struct "ShufflePlaylistOptions".
16
+ */
17
+ @DoNotStrip
18
+ @Keep
19
+ data class ShufflePlaylistOptions(
20
+ @DoNotStrip
21
+ @Keep
22
+ val keepCurrentTrackFirst: Boolean?
23
+ ) {
24
+ /* primary constructor */
25
+
26
+ companion object {
27
+ /**
28
+ * Constructor called from C++
29
+ */
30
+ @DoNotStrip
31
+ @Keep
32
+ @Suppress("unused")
33
+ @JvmStatic
34
+ private fun fromCpp(keepCurrentTrackFirst: Boolean?): ShufflePlaylistOptions {
35
+ return ShufflePlaylistOptions(keepCurrentTrackFirst)
36
+ }
37
+ }
38
+ }