react-native-nitro-player 1.6.0-alpha.1 → 1.7.0-alpha.0
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 +6 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +9 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +23 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +23 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +34 -17
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +6 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +22 -22
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +78 -7
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +19 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +33 -19
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +0 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +9 -5
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +96 -56
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadWorker.kt +62 -22
- package/android/src/main/java/com/margelo/nitro/nitroplayer/download/TrackItemJson.kt +60 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +9 -9
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/AuthAwareHttpDataSourceFactory.kt +12 -3
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/ExoPlayerBuilder.kt +4 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +99 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +22 -1
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +85 -70
- package/android/src/main/res/values/strings.xml +7 -0
- package/ios/HybridDownloadManager.swift +13 -3
- package/ios/HybridTrackPlayer.swift +8 -2
- package/ios/core/TrackPlayerCast.swift +32 -1
- package/ios/core/TrackPlayerCore.swift +19 -3
- package/ios/core/TrackPlayerListener.swift +15 -16
- package/ios/core/TrackPlayerNotify.swift +11 -11
- package/ios/core/TrackPlayerPlayback.swift +54 -16
- package/ios/core/TrackPlayerQueueBuild.swift +14 -2
- package/ios/core/TrackPlayerTempQueue.swift +1 -0
- package/ios/core/TrackPlayerUrlLoader.swift +11 -3
- package/ios/download/DownloadDatabase.swift +16 -5
- package/ios/download/DownloadFileManager.swift +12 -15
- package/ios/download/DownloadManagerCore.swift +61 -16
- package/ios/equalizer/EqualizerCore.swift +37 -9
- package/ios/media/MediaSessionManager.swift +89 -4
- package/ios/playlist/PlaylistManager.swift +47 -46
- package/ios/queue/HybridPlayerQueue.swift +28 -7
- package/lib/hooks/callbackManager.d.ts +9 -0
- package/lib/hooks/callbackManager.js +33 -0
- package/lib/hooks/useActualQueue.js +18 -6
- package/lib/hooks/useAndroidAutoConnection.js +2 -4
- package/lib/hooks/useAudioDevices.js +21 -24
- package/lib/hooks/useDownloadActions.js +33 -11
- package/lib/hooks/useDownloadProgress.js +20 -14
- package/lib/hooks/useDownloadStorage.js +1 -1
- package/lib/hooks/useNowPlaying.js +32 -6
- package/lib/hooks/useOnSeek.js +2 -2
- package/lib/hooks/usePlaylist.js +6 -15
- package/lib/specs/TrackPlayer.nitro.d.ts +6 -2
- package/lib/types/PlayerQueue.d.ts +8 -0
- package/nitrogen/generated/android/NitroPlayerOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JFunc_void_TrackItem.hpp +85 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +5 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +1 -0
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +9 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_TrackItem.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +9 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +8 -2
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.cpp +8 -0
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +22 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +6 -0
- package/nitrogen/generated/ios/swift/Func_void_TrackItem.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +16 -0
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +37 -1
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +1 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +1 -0
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +9 -1
- package/package.json +1 -1
- package/src/hooks/callbackManager.ts +44 -0
- package/src/hooks/useActualQueue.ts +18 -6
- package/src/hooks/useAndroidAutoConnection.ts +6 -6
- package/src/hooks/useAudioDevices.ts +23 -24
- package/src/hooks/useDownloadActions.ts +35 -11
- package/src/hooks/useDownloadProgress.ts +22 -19
- package/src/hooks/useDownloadStorage.ts +4 -3
- package/src/hooks/useNowPlaying.ts +34 -6
- package/src/hooks/useOnSeek.ts +2 -2
- package/src/hooks/usePlaylist.ts +6 -14
- package/src/specs/TrackPlayer.nitro.ts +6 -2
- package/src/types/PlayerQueue.ts +8 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/NitroPlayerNotificationProvider.kt +0 -200
- package/android/src/main/java/com/margelo/nitro/nitroplayer/queue/Queue.kt +0 -94
- package/android/src/main/java/com/margelo/nitro/nitroplayer/queue/QueueManager.kt +0 -143
package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_TrackItem.kt
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// Func_void_TrackItem.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.jni.HybridData
|
|
12
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
13
|
+
import dalvik.annotation.optimization.FastNative
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Represents the JavaScript callback `(track: struct) => void`.
|
|
18
|
+
* This can be either implemented in C++ (in which case it might be a callback coming from JS),
|
|
19
|
+
* or in Kotlin/Java (in which case it is a native callback).
|
|
20
|
+
*/
|
|
21
|
+
@DoNotStrip
|
|
22
|
+
@Keep
|
|
23
|
+
@Suppress("ClassName", "RedundantUnitReturnType")
|
|
24
|
+
fun interface Func_void_TrackItem: (TrackItem) -> Unit {
|
|
25
|
+
/**
|
|
26
|
+
* Call the given JS callback.
|
|
27
|
+
* @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted.
|
|
28
|
+
*/
|
|
29
|
+
@DoNotStrip
|
|
30
|
+
@Keep
|
|
31
|
+
override fun invoke(track: TrackItem): Unit
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Represents the JavaScript callback `(track: struct) => void`.
|
|
36
|
+
* This is implemented in C++, via a `std::function<...>`.
|
|
37
|
+
* The callback might be coming from JS.
|
|
38
|
+
*/
|
|
39
|
+
@DoNotStrip
|
|
40
|
+
@Keep
|
|
41
|
+
@Suppress(
|
|
42
|
+
"KotlinJniMissingFunction", "unused",
|
|
43
|
+
"RedundantSuppression", "RedundantUnitReturnType", "FunctionName",
|
|
44
|
+
"ConvertSecondaryConstructorToPrimary", "ClassName", "LocalVariableName",
|
|
45
|
+
)
|
|
46
|
+
class Func_void_TrackItem_cxx: Func_void_TrackItem {
|
|
47
|
+
@DoNotStrip
|
|
48
|
+
@Keep
|
|
49
|
+
private val mHybridData: HybridData
|
|
50
|
+
|
|
51
|
+
@DoNotStrip
|
|
52
|
+
@Keep
|
|
53
|
+
private constructor(hybridData: HybridData) {
|
|
54
|
+
mHybridData = hybridData
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@DoNotStrip
|
|
58
|
+
@Keep
|
|
59
|
+
override fun invoke(track: TrackItem): Unit
|
|
60
|
+
= invoke_cxx(track)
|
|
61
|
+
|
|
62
|
+
@FastNative
|
|
63
|
+
private external fun invoke_cxx(track: TrackItem): Unit
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Represents the JavaScript callback `(track: struct) => void`.
|
|
68
|
+
* This is implemented in Java/Kotlin, via a `(TrackItem) -> Unit`.
|
|
69
|
+
* The callback is always coming from native.
|
|
70
|
+
*/
|
|
71
|
+
@DoNotStrip
|
|
72
|
+
@Keep
|
|
73
|
+
@Suppress("ClassName", "RedundantUnitReturnType", "unused")
|
|
74
|
+
class Func_void_TrackItem_java(private val function: (TrackItem) -> Unit): Func_void_TrackItem {
|
|
75
|
+
@DoNotStrip
|
|
76
|
+
@Keep
|
|
77
|
+
override fun invoke(track: TrackItem): Unit {
|
|
78
|
+
return this.function(track)
|
|
79
|
+
}
|
|
80
|
+
}
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt
CHANGED
|
@@ -130,6 +130,15 @@ abstract class HybridTrackPlayerSpec: HybridObject() {
|
|
|
130
130
|
return __result
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
abstract fun appStartedWithNotification(callback: (track: TrackItem) -> Unit): Unit
|
|
134
|
+
|
|
135
|
+
@DoNotStrip
|
|
136
|
+
@Keep
|
|
137
|
+
private fun appStartedWithNotification_cxx(callback: Func_void_TrackItem): Unit {
|
|
138
|
+
val __result = appStartedWithNotification(callback)
|
|
139
|
+
return __result
|
|
140
|
+
}
|
|
141
|
+
|
|
133
142
|
abstract fun onAndroidAutoConnectionChange(callback: (connected: Boolean) -> Unit): Unit
|
|
134
143
|
|
|
135
144
|
@DoNotStrip
|
|
@@ -28,6 +28,12 @@ data class PlayerConfig(
|
|
|
28
28
|
val showInNotification: Boolean?,
|
|
29
29
|
@DoNotStrip
|
|
30
30
|
@Keep
|
|
31
|
+
val remoteSkipForwardInterval: Double?,
|
|
32
|
+
@DoNotStrip
|
|
33
|
+
@Keep
|
|
34
|
+
val remoteSkipBackwardInterval: Double?,
|
|
35
|
+
@DoNotStrip
|
|
36
|
+
@Keep
|
|
31
37
|
val lookaheadCount: Double?,
|
|
32
38
|
@DoNotStrip
|
|
33
39
|
@Keep
|
|
@@ -43,8 +49,8 @@ data class PlayerConfig(
|
|
|
43
49
|
@Keep
|
|
44
50
|
@Suppress("unused")
|
|
45
51
|
@JvmStatic
|
|
46
|
-
private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, lookaheadCount: Double?, androidNotificationIcon: String?): PlayerConfig {
|
|
47
|
-
return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, lookaheadCount, androidNotificationIcon)
|
|
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)
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
}
|
|
@@ -323,6 +323,14 @@ namespace margelo::nitro::nitroplayer::bridge::swift {
|
|
|
323
323
|
};
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
// pragma MARK: std::function<void(const TrackItem& /* track */)>
|
|
327
|
+
Func_void_TrackItem create_Func_void_TrackItem(void* NON_NULL swiftClosureWrapper) noexcept {
|
|
328
|
+
auto swiftClosure = NitroPlayer::Func_void_TrackItem::fromUnsafe(swiftClosureWrapper);
|
|
329
|
+
return [swiftClosure = std::move(swiftClosure)](const TrackItem& track) mutable -> void {
|
|
330
|
+
swiftClosure.call(track);
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
326
334
|
// pragma MARK: std::function<void(const std::vector<TrackItem>& /* tracks */, double /* lookahead */)>
|
|
327
335
|
Func_void_std__vector_TrackItem__double create_Func_void_std__vector_TrackItem__double(void* NON_NULL swiftClosureWrapper) noexcept {
|
|
328
336
|
auto swiftClosure = NitroPlayer::Func_void_std__vector_TrackItem__double::fromUnsafe(swiftClosureWrapper);
|
|
@@ -1752,6 +1752,28 @@ namespace margelo::nitro::nitroplayer::bridge::swift {
|
|
|
1752
1752
|
return Func_void_TimedMetadata_Wrapper(std::move(value));
|
|
1753
1753
|
}
|
|
1754
1754
|
|
|
1755
|
+
// pragma MARK: std::function<void(const TrackItem& /* track */)>
|
|
1756
|
+
/**
|
|
1757
|
+
* Specialized version of `std::function<void(const TrackItem&)>`.
|
|
1758
|
+
*/
|
|
1759
|
+
using Func_void_TrackItem = std::function<void(const TrackItem& /* track */)>;
|
|
1760
|
+
/**
|
|
1761
|
+
* Wrapper class for a `std::function<void(const TrackItem& / * track * /)>`, this can be used from Swift.
|
|
1762
|
+
*/
|
|
1763
|
+
class Func_void_TrackItem_Wrapper final {
|
|
1764
|
+
public:
|
|
1765
|
+
explicit Func_void_TrackItem_Wrapper(std::function<void(const TrackItem& /* track */)>&& func): _function(std::make_unique<std::function<void(const TrackItem& /* track */)>>(std::move(func))) {}
|
|
1766
|
+
inline void call(TrackItem track) const noexcept {
|
|
1767
|
+
_function->operator()(track);
|
|
1768
|
+
}
|
|
1769
|
+
private:
|
|
1770
|
+
std::unique_ptr<std::function<void(const TrackItem& /* track */)>> _function;
|
|
1771
|
+
} SWIFT_NONCOPYABLE;
|
|
1772
|
+
Func_void_TrackItem create_Func_void_TrackItem(void* NON_NULL swiftClosureWrapper) noexcept;
|
|
1773
|
+
inline Func_void_TrackItem_Wrapper wrap_Func_void_TrackItem(Func_void_TrackItem value) noexcept {
|
|
1774
|
+
return Func_void_TrackItem_Wrapper(std::move(value));
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1755
1777
|
// pragma MARK: std::function<void(const std::vector<TrackItem>& /* tracks */, double /* lookahead */)>
|
|
1756
1778
|
/**
|
|
1757
1779
|
* Specialized version of `std::function<void(const std::vector<TrackItem>&, double)>`.
|
|
@@ -238,6 +238,12 @@ namespace margelo::nitro::nitroplayer {
|
|
|
238
238
|
std::rethrow_exception(__result.error());
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
|
+
inline void appStartedWithNotification(const std::function<void(const TrackItem& /* track */)>& callback) override {
|
|
242
|
+
auto __result = _swiftPart.appStartedWithNotification(callback);
|
|
243
|
+
if (__result.hasError()) [[unlikely]] {
|
|
244
|
+
std::rethrow_exception(__result.error());
|
|
245
|
+
}
|
|
246
|
+
}
|
|
241
247
|
inline void onAndroidAutoConnectionChange(const std::function<void(bool /* connected */)>& callback) override {
|
|
242
248
|
auto __result = _swiftPart.onAndroidAutoConnectionChange(callback);
|
|
243
249
|
if (__result.hasError()) [[unlikely]] {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// Func_void_TrackItem.swift
|
|
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
|
+
import NitroModules
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wraps a Swift `(_ track: TrackItem) -> Void` as a class.
|
|
12
|
+
* This class can be used from C++, e.g. to wrap the Swift closure as a `std::function`.
|
|
13
|
+
*/
|
|
14
|
+
public final class Func_void_TrackItem {
|
|
15
|
+
public typealias bridge = margelo.nitro.nitroplayer.bridge.swift
|
|
16
|
+
|
|
17
|
+
private let closure: (_ track: TrackItem) -> Void
|
|
18
|
+
|
|
19
|
+
public init(_ closure: @escaping (_ track: TrackItem) -> Void) {
|
|
20
|
+
self.closure = closure
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@inline(__always)
|
|
24
|
+
public func call(track: TrackItem) -> Void {
|
|
25
|
+
self.closure(track)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Casts this instance to a retained unsafe raw pointer.
|
|
30
|
+
* This acquires one additional strong reference on the object!
|
|
31
|
+
*/
|
|
32
|
+
@inline(__always)
|
|
33
|
+
public func toUnsafe() -> UnsafeMutableRawPointer {
|
|
34
|
+
return Unmanaged.passRetained(self).toOpaque()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Casts an unsafe pointer to a `Func_void_TrackItem`.
|
|
39
|
+
* The pointer has to be a retained opaque `Unmanaged<Func_void_TrackItem>`.
|
|
40
|
+
* This removes one strong reference from the object!
|
|
41
|
+
*/
|
|
42
|
+
@inline(__always)
|
|
43
|
+
public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> Func_void_TrackItem {
|
|
44
|
+
return Unmanaged<Func_void_TrackItem>.fromOpaque(pointer).takeRetainedValue()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -32,6 +32,7 @@ public protocol HybridTrackPlayerSpec_protocol: HybridObject {
|
|
|
32
32
|
func onSeek(callback: @escaping (_ position: Double, _ totalDuration: Double) -> Void) throws -> Void
|
|
33
33
|
func onPlaybackProgressChange(callback: @escaping (_ position: Double, _ totalDuration: Double, _ isManuallySeeked: Bool?) -> Void) throws -> Void
|
|
34
34
|
func onTimedMetadata(callback: @escaping (_ metadata: TimedMetadata) -> Void) throws -> Void
|
|
35
|
+
func appStartedWithNotification(callback: @escaping (_ track: TrackItem) -> Void) throws -> Void
|
|
35
36
|
func onAndroidAutoConnectionChange(callback: @escaping (_ connected: Bool) -> Void) throws -> Void
|
|
36
37
|
func isAndroidAutoConnected() throws -> Bool
|
|
37
38
|
func setVolume(volume: Double) throws -> Promise<Void>
|
|
@@ -494,6 +494,22 @@ open class HybridTrackPlayerSpec_cxx {
|
|
|
494
494
|
}
|
|
495
495
|
}
|
|
496
496
|
|
|
497
|
+
@inline(__always)
|
|
498
|
+
public final func appStartedWithNotification(callback: bridge.Func_void_TrackItem) -> bridge.Result_void_ {
|
|
499
|
+
do {
|
|
500
|
+
try self.__implementation.appStartedWithNotification(callback: { () -> (TrackItem) -> Void in
|
|
501
|
+
let __wrappedFunction = bridge.wrap_Func_void_TrackItem(callback)
|
|
502
|
+
return { (__track: TrackItem) -> Void in
|
|
503
|
+
__wrappedFunction.call(__track)
|
|
504
|
+
}
|
|
505
|
+
}())
|
|
506
|
+
return bridge.create_Result_void_()
|
|
507
|
+
} catch (let __error) {
|
|
508
|
+
let __exceptionPtr = __error.toCpp()
|
|
509
|
+
return bridge.create_Result_void_(__exceptionPtr)
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
497
513
|
@inline(__always)
|
|
498
514
|
public final func onAndroidAutoConnectionChange(callback: bridge.Func_void_bool) -> bridge.Result_void_ {
|
|
499
515
|
do {
|
|
@@ -18,7 +18,7 @@ public extension PlayerConfig {
|
|
|
18
18
|
/**
|
|
19
19
|
* Create a new instance of `PlayerConfig`.
|
|
20
20
|
*/
|
|
21
|
-
init(androidAutoEnabled: Bool?, carPlayEnabled: Bool?, showInNotification: Bool?, lookaheadCount: Double?, androidNotificationIcon: String?) {
|
|
21
|
+
init(androidAutoEnabled: Bool?, carPlayEnabled: Bool?, showInNotification: Bool?, remoteSkipForwardInterval: Double?, remoteSkipBackwardInterval: Double?, lookaheadCount: Double?, androidNotificationIcon: String?) {
|
|
22
22
|
self.init({ () -> bridge.std__optional_bool_ in
|
|
23
23
|
if let __unwrappedValue = androidAutoEnabled {
|
|
24
24
|
return bridge.create_std__optional_bool_(__unwrappedValue)
|
|
@@ -37,6 +37,18 @@ public extension PlayerConfig {
|
|
|
37
37
|
} else {
|
|
38
38
|
return .init()
|
|
39
39
|
}
|
|
40
|
+
}(), { () -> bridge.std__optional_double_ in
|
|
41
|
+
if let __unwrappedValue = remoteSkipForwardInterval {
|
|
42
|
+
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
43
|
+
} else {
|
|
44
|
+
return .init()
|
|
45
|
+
}
|
|
46
|
+
}(), { () -> bridge.std__optional_double_ in
|
|
47
|
+
if let __unwrappedValue = remoteSkipBackwardInterval {
|
|
48
|
+
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
49
|
+
} else {
|
|
50
|
+
return .init()
|
|
51
|
+
}
|
|
40
52
|
}(), { () -> bridge.std__optional_double_ in
|
|
41
53
|
if let __unwrappedValue = lookaheadCount {
|
|
42
54
|
return bridge.create_std__optional_double_(__unwrappedValue)
|
|
@@ -88,6 +100,30 @@ public extension PlayerConfig {
|
|
|
88
100
|
}()
|
|
89
101
|
}
|
|
90
102
|
|
|
103
|
+
@inline(__always)
|
|
104
|
+
var remoteSkipForwardInterval: Double? {
|
|
105
|
+
return { () -> Double? in
|
|
106
|
+
if bridge.has_value_std__optional_double_(self.__remoteSkipForwardInterval) {
|
|
107
|
+
let __unwrapped = bridge.get_std__optional_double_(self.__remoteSkipForwardInterval)
|
|
108
|
+
return __unwrapped
|
|
109
|
+
} else {
|
|
110
|
+
return nil
|
|
111
|
+
}
|
|
112
|
+
}()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@inline(__always)
|
|
116
|
+
var remoteSkipBackwardInterval: Double? {
|
|
117
|
+
return { () -> Double? in
|
|
118
|
+
if bridge.has_value_std__optional_double_(self.__remoteSkipBackwardInterval) {
|
|
119
|
+
let __unwrapped = bridge.get_std__optional_double_(self.__remoteSkipBackwardInterval)
|
|
120
|
+
return __unwrapped
|
|
121
|
+
} else {
|
|
122
|
+
return nil
|
|
123
|
+
}
|
|
124
|
+
}()
|
|
125
|
+
}
|
|
126
|
+
|
|
91
127
|
@inline(__always)
|
|
92
128
|
var lookaheadCount: Double? {
|
|
93
129
|
return { () -> Double? in
|
|
@@ -33,6 +33,7 @@ namespace margelo::nitro::nitroplayer {
|
|
|
33
33
|
prototype.registerHybridMethod("onSeek", &HybridTrackPlayerSpec::onSeek);
|
|
34
34
|
prototype.registerHybridMethod("onPlaybackProgressChange", &HybridTrackPlayerSpec::onPlaybackProgressChange);
|
|
35
35
|
prototype.registerHybridMethod("onTimedMetadata", &HybridTrackPlayerSpec::onTimedMetadata);
|
|
36
|
+
prototype.registerHybridMethod("appStartedWithNotification", &HybridTrackPlayerSpec::appStartedWithNotification);
|
|
36
37
|
prototype.registerHybridMethod("onAndroidAutoConnectionChange", &HybridTrackPlayerSpec::onAndroidAutoConnectionChange);
|
|
37
38
|
prototype.registerHybridMethod("isAndroidAutoConnected", &HybridTrackPlayerSpec::isAndroidAutoConnected);
|
|
38
39
|
prototype.registerHybridMethod("setVolume", &HybridTrackPlayerSpec::setVolume);
|
|
@@ -91,6 +91,7 @@ namespace margelo::nitro::nitroplayer {
|
|
|
91
91
|
virtual void onSeek(const std::function<void(double /* position */, double /* totalDuration */)>& callback) = 0;
|
|
92
92
|
virtual void onPlaybackProgressChange(const std::function<void(double /* position */, double /* totalDuration */, std::optional<bool> /* isManuallySeeked */)>& callback) = 0;
|
|
93
93
|
virtual void onTimedMetadata(const std::function<void(const TimedMetadata& /* metadata */)>& callback) = 0;
|
|
94
|
+
virtual void appStartedWithNotification(const std::function<void(const TrackItem& /* track */)>& callback) = 0;
|
|
94
95
|
virtual void onAndroidAutoConnectionChange(const std::function<void(bool /* connected */)>& callback) = 0;
|
|
95
96
|
virtual bool isAndroidAutoConnected() = 0;
|
|
96
97
|
virtual std::shared_ptr<Promise<void>> setVolume(double volume) = 0;
|
|
@@ -43,12 +43,14 @@ namespace margelo::nitro::nitroplayer {
|
|
|
43
43
|
std::optional<bool> androidAutoEnabled SWIFT_PRIVATE;
|
|
44
44
|
std::optional<bool> carPlayEnabled SWIFT_PRIVATE;
|
|
45
45
|
std::optional<bool> showInNotification SWIFT_PRIVATE;
|
|
46
|
+
std::optional<double> remoteSkipForwardInterval SWIFT_PRIVATE;
|
|
47
|
+
std::optional<double> remoteSkipBackwardInterval SWIFT_PRIVATE;
|
|
46
48
|
std::optional<double> lookaheadCount SWIFT_PRIVATE;
|
|
47
49
|
std::optional<std::string> androidNotificationIcon SWIFT_PRIVATE;
|
|
48
50
|
|
|
49
51
|
public:
|
|
50
52
|
PlayerConfig() = default;
|
|
51
|
-
explicit PlayerConfig(std::optional<bool> androidAutoEnabled, std::optional<bool> carPlayEnabled, std::optional<bool> showInNotification, std::optional<double> lookaheadCount, std::optional<std::string> androidNotificationIcon): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), lookaheadCount(lookaheadCount), androidNotificationIcon(androidNotificationIcon) {}
|
|
53
|
+
explicit PlayerConfig(std::optional<bool> androidAutoEnabled, std::optional<bool> carPlayEnabled, std::optional<bool> showInNotification, std::optional<double> remoteSkipForwardInterval, std::optional<double> remoteSkipBackwardInterval, std::optional<double> lookaheadCount, std::optional<std::string> androidNotificationIcon): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), remoteSkipForwardInterval(remoteSkipForwardInterval), remoteSkipBackwardInterval(remoteSkipBackwardInterval), lookaheadCount(lookaheadCount), androidNotificationIcon(androidNotificationIcon) {}
|
|
52
54
|
|
|
53
55
|
public:
|
|
54
56
|
friend bool operator==(const PlayerConfig& lhs, const PlayerConfig& rhs) = default;
|
|
@@ -67,6 +69,8 @@ namespace margelo::nitro {
|
|
|
67
69
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidAutoEnabled"))),
|
|
68
70
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled"))),
|
|
69
71
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"))),
|
|
72
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipForwardInterval"))),
|
|
73
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipBackwardInterval"))),
|
|
70
74
|
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"))),
|
|
71
75
|
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))
|
|
72
76
|
);
|
|
@@ -76,6 +80,8 @@ namespace margelo::nitro {
|
|
|
76
80
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidAutoEnabled"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.androidAutoEnabled));
|
|
77
81
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.carPlayEnabled));
|
|
78
82
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.showInNotification));
|
|
83
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipForwardInterval"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.remoteSkipForwardInterval));
|
|
84
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipBackwardInterval"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.remoteSkipBackwardInterval));
|
|
79
85
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.lookaheadCount));
|
|
80
86
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.androidNotificationIcon));
|
|
81
87
|
return obj;
|
|
@@ -91,6 +97,8 @@ namespace margelo::nitro {
|
|
|
91
97
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidAutoEnabled")))) return false;
|
|
92
98
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled")))) return false;
|
|
93
99
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification")))) return false;
|
|
100
|
+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipForwardInterval")))) return false;
|
|
101
|
+
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipBackwardInterval")))) return false;
|
|
94
102
|
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount")))) return false;
|
|
95
103
|
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))) return false;
|
|
96
104
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-player",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0-alpha.0",
|
|
4
4
|
"description": "A powerful audio player library for React Native with playlist management, playback controls, and support for Android Auto and CarPlay",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -19,6 +19,7 @@ type TemporaryQueueCallback = (
|
|
|
19
19
|
playNextQueue: TrackItem[],
|
|
20
20
|
upNextQueue: TrackItem[]
|
|
21
21
|
) => void
|
|
22
|
+
type AndroidAutoConnectionCallback = (connected: boolean) => void
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Internal subscription manager that allows multiple hooks to subscribe
|
|
@@ -33,7 +34,10 @@ class CallbackSubscriptionManager {
|
|
|
33
34
|
private timedMetadataSubscribers = new Set<TimedMetadataCallback>()
|
|
34
35
|
private temporaryQueueSubscribers = new Set<TemporaryQueueCallback>()
|
|
35
36
|
private playlistsChangedSubscribers = new Set<() => void>()
|
|
37
|
+
private androidAutoConnectionSubscribers =
|
|
38
|
+
new Set<AndroidAutoConnectionCallback>()
|
|
36
39
|
private isPlaylistsChangedRegistered = false
|
|
40
|
+
private isAndroidAutoConnectionRegistered = false
|
|
37
41
|
private isPlaybackStateRegistered = false
|
|
38
42
|
private isTrackChangeRegistered = false
|
|
39
43
|
private isPlaybackProgressRegistered = false
|
|
@@ -272,6 +276,46 @@ class CallbackSubscriptionManager {
|
|
|
272
276
|
}
|
|
273
277
|
}
|
|
274
278
|
|
|
279
|
+
/**
|
|
280
|
+
* Subscribe to Android Auto connection changes
|
|
281
|
+
* @returns Unsubscribe function
|
|
282
|
+
*/
|
|
283
|
+
subscribeToAndroidAutoConnection(
|
|
284
|
+
callback: AndroidAutoConnectionCallback
|
|
285
|
+
): () => void {
|
|
286
|
+
this.androidAutoConnectionSubscribers.add(callback)
|
|
287
|
+
this.ensureAndroidAutoConnectionRegistered()
|
|
288
|
+
|
|
289
|
+
return () => {
|
|
290
|
+
this.androidAutoConnectionSubscribers.delete(callback)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private ensureAndroidAutoConnectionRegistered(): void {
|
|
295
|
+
if (this.isAndroidAutoConnectionRegistered) return
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
TrackPlayer.onAndroidAutoConnectionChange((connected) => {
|
|
299
|
+
this.androidAutoConnectionSubscribers.forEach((subscriber) => {
|
|
300
|
+
try {
|
|
301
|
+
subscriber(connected)
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error(
|
|
304
|
+
'[CallbackManager] Error in Android Auto connection subscriber:',
|
|
305
|
+
error
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
this.isAndroidAutoConnectionRegistered = true
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error(
|
|
313
|
+
'[CallbackManager] Failed to register Android Auto connection callback:',
|
|
314
|
+
error
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
275
319
|
/**
|
|
276
320
|
* Subscribe to in-stream (timed) metadata events
|
|
277
321
|
* @returns Unsubscribe function
|
|
@@ -73,6 +73,18 @@ export function useActualQueue(): UseActualQueueResult {
|
|
|
73
73
|
}
|
|
74
74
|
}, [])
|
|
75
75
|
|
|
76
|
+
// Skips involving temp tracks fire track-change and temporary-queue-change
|
|
77
|
+
// together; the gate collapses the burst into one getActualQueue() fetch
|
|
78
|
+
const fetchScheduled = useRef(false)
|
|
79
|
+
const scheduleUpdate = useCallback(() => {
|
|
80
|
+
if (fetchScheduled.current) return
|
|
81
|
+
fetchScheduled.current = true
|
|
82
|
+
queueMicrotask(() => {
|
|
83
|
+
fetchScheduled.current = false
|
|
84
|
+
updateQueue()
|
|
85
|
+
})
|
|
86
|
+
}, [updateQueue])
|
|
87
|
+
|
|
76
88
|
const refreshQueue = useCallback(() => {
|
|
77
89
|
if (!isMounted.current) return
|
|
78
90
|
setIsLoading(true)
|
|
@@ -97,25 +109,25 @@ export function useActualQueue(): UseActualQueueResult {
|
|
|
97
109
|
// track changes and temporary-queue edits do.
|
|
98
110
|
useEffect(() => {
|
|
99
111
|
return callbackManager.subscribeToTrackChange(() => {
|
|
100
|
-
|
|
112
|
+
scheduleUpdate()
|
|
101
113
|
})
|
|
102
|
-
}, [
|
|
114
|
+
}, [scheduleUpdate])
|
|
103
115
|
|
|
104
116
|
// Update queue when playNext / upNext change (native pushes the new lists).
|
|
105
117
|
useEffect(() => {
|
|
106
118
|
return callbackManager.subscribeToTemporaryQueueChange(() => {
|
|
107
|
-
|
|
119
|
+
scheduleUpdate()
|
|
108
120
|
})
|
|
109
|
-
}, [
|
|
121
|
+
}, [scheduleUpdate])
|
|
110
122
|
|
|
111
123
|
// Update queue when the playlist itself is edited (track added / removed / reordered).
|
|
112
124
|
// Without this, a playlist mutation while paused would leave the queue stale until the
|
|
113
125
|
// next track change — playback-state changes used to paper over it.
|
|
114
126
|
useEffect(() => {
|
|
115
127
|
return callbackManager.subscribeToPlaylistsChanged(() => {
|
|
116
|
-
|
|
128
|
+
scheduleUpdate()
|
|
117
129
|
})
|
|
118
|
-
}, [
|
|
130
|
+
}, [scheduleUpdate])
|
|
119
131
|
|
|
120
132
|
return { queue, refreshQueue, isLoading }
|
|
121
133
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
2
|
import { TrackPlayer } from '../index'
|
|
3
|
+
import { callbackManager } from './callbackManager'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Hook to detect Android Auto connection status using the official Android for Cars API
|
|
@@ -15,15 +16,14 @@ export function useAndroidAutoConnection() {
|
|
|
15
16
|
const [isConnected, setIsConnected] = useState<boolean>(false)
|
|
16
17
|
|
|
17
18
|
useEffect(() => {
|
|
18
|
-
// Set initial state
|
|
19
19
|
const initialState = TrackPlayer.isAndroidAutoConnected()
|
|
20
20
|
setIsConnected(initialState)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
return callbackManager.subscribeToAndroidAutoConnection(
|
|
23
|
+
(connected: boolean) => {
|
|
24
|
+
setIsConnected(connected)
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
27
|
}, [])
|
|
28
28
|
|
|
29
29
|
return { isConnected }
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import { NitroModules } from 'react-native-nitro-modules'
|
|
4
|
-
import type { AudioDevices as AudioDevicesType } from '../specs/AudioDevices.nitro'
|
|
2
|
+
import { AudioDevices } from '../index'
|
|
5
3
|
import type { TAudioDevice } from '../specs/AudioDevices.nitro'
|
|
6
4
|
|
|
5
|
+
function devicesEqual(a: TAudioDevice[], b: TAudioDevice[]): boolean {
|
|
6
|
+
if (a.length !== b.length) return false
|
|
7
|
+
return a.every(
|
|
8
|
+
(d, i) => d.id === b[i]!.id && d.isActive === b[i]!.isActive
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* Hook to get audio devices (Android only)
|
|
9
14
|
*
|
|
@@ -30,34 +35,28 @@ export function useAudioDevices() {
|
|
|
30
35
|
const [devices, setDevices] = useState<TAudioDevice[]>([])
|
|
31
36
|
|
|
32
37
|
useEffect(() => {
|
|
33
|
-
if (
|
|
38
|
+
if (!AudioDevices) {
|
|
34
39
|
return undefined
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.error('Error getting audio devices:', error)
|
|
48
|
-
}
|
|
42
|
+
const updateDevices = () => {
|
|
43
|
+
try {
|
|
44
|
+
const currentDevices = AudioDevices!.getAudioDevices()
|
|
45
|
+
// Bail when nothing changed — the bridge returns a fresh array identity
|
|
46
|
+
// every tick, which would otherwise re-render every consumer at 0.5Hz
|
|
47
|
+
setDevices((prev) =>
|
|
48
|
+
devicesEqual(prev, currentDevices) ? prev : currentDevices
|
|
49
|
+
)
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Error getting audio devices:', error)
|
|
49
52
|
}
|
|
53
|
+
}
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
updateDevices()
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
const interval = setInterval(updateDevices, 2000)
|
|
57
|
+
const interval = setInterval(updateDevices, 2000)
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error('Error setting up audio devices polling:', error)
|
|
59
|
-
return undefined
|
|
60
|
-
}
|
|
59
|
+
return () => clearInterval(interval)
|
|
61
60
|
}, [])
|
|
62
61
|
|
|
63
62
|
return { devices }
|