react-native-nitro-player 1.4.2 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +2 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +44 -4
- package/ios/HybridTrackPlayer.swift +0 -1
- package/lib/types/PlayerQueue.d.ts +1 -0
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +8 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +6 -3
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +7 -2
- package/package.json +1 -1
- package/src/types/PlayerQueue.ts +2 -0
|
@@ -8,6 +8,7 @@ import com.margelo.nitro.nitroplayer.Reason
|
|
|
8
8
|
import com.margelo.nitro.nitroplayer.RepeatMode
|
|
9
9
|
import com.margelo.nitro.nitroplayer.TrackPlayerState
|
|
10
10
|
import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
|
|
11
|
+
import com.margelo.nitro.nitroplayer.media.NitroPlayerPlaybackService
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Playback control — all public functions are suspend and execute on the player thread
|
|
@@ -95,6 +96,7 @@ suspend fun TrackPlayerCore.configure(config: PlayerConfig) =
|
|
|
95
96
|
withPlayerContext {
|
|
96
97
|
config.androidAutoEnabled?.let { NitroPlayerMediaBrowserService.isAndroidAutoEnabled = it }
|
|
97
98
|
config.lookaheadCount?.let { lookaheadCount = it.toInt() }
|
|
99
|
+
config.androidNotificationIcon?.let { NitroPlayerPlaybackService.notificationSmallIconResName = it }
|
|
98
100
|
mediaSessionManager?.configure(config.androidAutoEnabled, config.carPlayEnabled, config.showInNotification)
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -7,6 +7,7 @@ import android.os.IBinder
|
|
|
7
7
|
import android.os.Looper
|
|
8
8
|
import androidx.media3.common.util.UnstableApi
|
|
9
9
|
import androidx.media3.exoplayer.ExoPlayer
|
|
10
|
+
import androidx.media3.session.DefaultMediaNotificationProvider
|
|
10
11
|
import androidx.media3.session.MediaSession
|
|
11
12
|
import androidx.media3.session.MediaSessionService
|
|
12
13
|
import com.margelo.nitro.nitroplayer.core.NitroPlayerLogger
|
|
@@ -29,11 +30,24 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
29
30
|
|
|
30
31
|
companion object {
|
|
31
32
|
const val ACTION_LOCAL_BIND = "com.margelo.nitro.nitroplayer.LOCAL_BIND"
|
|
33
|
+
|
|
34
|
+
@Volatile
|
|
35
|
+
var notificationSmallIconResName: String? = null
|
|
36
|
+
set(value) {
|
|
37
|
+
field = value
|
|
38
|
+
instance?.let { service ->
|
|
39
|
+
service.mainHandler.post { service.applyNotificationSmallIcon() }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Volatile
|
|
44
|
+
private var instance: NitroPlayerPlaybackService? = null
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
// ── Created in onCreate ────────────────────────────────────────────────
|
|
35
48
|
private lateinit var player: ExoPlayer
|
|
36
49
|
private var mediaSession: MediaSession? = null
|
|
50
|
+
private var notificationProvider: DefaultMediaNotificationProvider? = null
|
|
37
51
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
38
52
|
|
|
39
53
|
@Volatile
|
|
@@ -54,6 +68,7 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
54
68
|
override fun onCreate() {
|
|
55
69
|
super.onCreate()
|
|
56
70
|
NitroPlayerLogger.log("PlaybackService") { "onCreate" }
|
|
71
|
+
instance = this
|
|
57
72
|
|
|
58
73
|
// Build ExoPlayer on main looper (default)
|
|
59
74
|
player = ExoPlayerBuilder.build(this)
|
|
@@ -65,12 +80,35 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
65
80
|
.setCallback(MediaSessionCallbackFactory.create(this, playlistManager))
|
|
66
81
|
.build()
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
val provider = DefaultMediaNotificationProvider.Builder(this).build()
|
|
84
|
+
notificationProvider = provider
|
|
85
|
+
applyNotificationSmallIcon()
|
|
86
|
+
setMediaNotificationProvider(provider)
|
|
87
|
+
|
|
71
88
|
addSession(mediaSession!!)
|
|
89
|
+
}
|
|
72
90
|
|
|
73
|
-
|
|
91
|
+
private fun applyNotificationSmallIcon() {
|
|
92
|
+
val name = notificationSmallIconResName ?: return
|
|
93
|
+
val provider = notificationProvider ?: return
|
|
94
|
+
val resId = resources.getIdentifier(name, "drawable", packageName)
|
|
95
|
+
if (resId == 0) {
|
|
96
|
+
NitroPlayerLogger.log("PlaybackService") {
|
|
97
|
+
"androidNotificationIcon '$name' not found in drawable resources — using default"
|
|
98
|
+
}
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
provider.setSmallIcon(resId)
|
|
102
|
+
|
|
103
|
+
// Live Refresh :)
|
|
104
|
+
val session = mediaSession
|
|
105
|
+
if (session != null && session.player.isPlaying) {
|
|
106
|
+
try {
|
|
107
|
+
onUpdateNotification(session, false)
|
|
108
|
+
} catch (e: Exception) {
|
|
109
|
+
NitroPlayerLogger.log("PlaybackService") { "notification refresh failed: ${e.message}" }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
74
112
|
}
|
|
75
113
|
|
|
76
114
|
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
|
|
@@ -93,6 +131,8 @@ class NitroPlayerPlaybackService : MediaSessionService() {
|
|
|
93
131
|
|
|
94
132
|
override fun onDestroy() {
|
|
95
133
|
NitroPlayerLogger.log("PlaybackService") { "onDestroy" }
|
|
134
|
+
if (instance === this) instance = null
|
|
135
|
+
notificationProvider = null
|
|
96
136
|
mediaSession?.release()
|
|
97
137
|
mediaSession = null
|
|
98
138
|
player.release()
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include "PlayerConfig.hpp"
|
|
12
12
|
|
|
13
13
|
#include <optional>
|
|
14
|
+
#include <string>
|
|
14
15
|
|
|
15
16
|
namespace margelo::nitro::nitroplayer {
|
|
16
17
|
|
|
@@ -39,11 +40,14 @@ namespace margelo::nitro::nitroplayer {
|
|
|
39
40
|
jni::local_ref<jni::JBoolean> showInNotification = this->getFieldValue(fieldShowInNotification);
|
|
40
41
|
static const auto fieldLookaheadCount = clazz->getField<jni::JDouble>("lookaheadCount");
|
|
41
42
|
jni::local_ref<jni::JDouble> lookaheadCount = this->getFieldValue(fieldLookaheadCount);
|
|
43
|
+
static const auto fieldAndroidNotificationIcon = clazz->getField<jni::JString>("androidNotificationIcon");
|
|
44
|
+
jni::local_ref<jni::JString> androidNotificationIcon = this->getFieldValue(fieldAndroidNotificationIcon);
|
|
42
45
|
return PlayerConfig(
|
|
43
46
|
androidAutoEnabled != nullptr ? std::make_optional(static_cast<bool>(androidAutoEnabled->value())) : std::nullopt,
|
|
44
47
|
carPlayEnabled != nullptr ? std::make_optional(static_cast<bool>(carPlayEnabled->value())) : std::nullopt,
|
|
45
48
|
showInNotification != nullptr ? std::make_optional(static_cast<bool>(showInNotification->value())) : std::nullopt,
|
|
46
|
-
lookaheadCount != nullptr ? std::make_optional(lookaheadCount->value()) : std::nullopt
|
|
49
|
+
lookaheadCount != nullptr ? std::make_optional(lookaheadCount->value()) : std::nullopt,
|
|
50
|
+
androidNotificationIcon != nullptr ? std::make_optional(androidNotificationIcon->toStdString()) : std::nullopt
|
|
47
51
|
);
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -53,7 +57,7 @@ namespace margelo::nitro::nitroplayer {
|
|
|
53
57
|
*/
|
|
54
58
|
[[maybe_unused]]
|
|
55
59
|
static jni::local_ref<JPlayerConfig::javaobject> fromCpp(const PlayerConfig& value) {
|
|
56
|
-
using JSignature = JPlayerConfig(jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JBoolean>, jni::alias_ref<jni::JDouble>);
|
|
60
|
+
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::JString>);
|
|
57
61
|
static const auto clazz = javaClassStatic();
|
|
58
62
|
static const auto create = clazz->getStaticMethod<JSignature>("fromCpp");
|
|
59
63
|
return create(
|
|
@@ -61,7 +65,8 @@ namespace margelo::nitro::nitroplayer {
|
|
|
61
65
|
value.androidAutoEnabled.has_value() ? jni::JBoolean::valueOf(value.androidAutoEnabled.value()) : nullptr,
|
|
62
66
|
value.carPlayEnabled.has_value() ? jni::JBoolean::valueOf(value.carPlayEnabled.value()) : nullptr,
|
|
63
67
|
value.showInNotification.has_value() ? jni::JBoolean::valueOf(value.showInNotification.value()) : nullptr,
|
|
64
|
-
value.lookaheadCount.has_value() ? jni::JDouble::valueOf(value.lookaheadCount.value()) : nullptr
|
|
68
|
+
value.lookaheadCount.has_value() ? jni::JDouble::valueOf(value.lookaheadCount.value()) : nullptr,
|
|
69
|
+
value.androidNotificationIcon.has_value() ? jni::make_jstring(value.androidNotificationIcon.value()) : nullptr
|
|
65
70
|
);
|
|
66
71
|
}
|
|
67
72
|
};
|
|
@@ -28,7 +28,10 @@ data class PlayerConfig(
|
|
|
28
28
|
val showInNotification: Boolean?,
|
|
29
29
|
@DoNotStrip
|
|
30
30
|
@Keep
|
|
31
|
-
val lookaheadCount: Double
|
|
31
|
+
val lookaheadCount: Double?,
|
|
32
|
+
@DoNotStrip
|
|
33
|
+
@Keep
|
|
34
|
+
val androidNotificationIcon: String?
|
|
32
35
|
) {
|
|
33
36
|
/* primary constructor */
|
|
34
37
|
|
|
@@ -40,8 +43,8 @@ data class PlayerConfig(
|
|
|
40
43
|
@Keep
|
|
41
44
|
@Suppress("unused")
|
|
42
45
|
@JvmStatic
|
|
43
|
-
private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, lookaheadCount: Double?): PlayerConfig {
|
|
44
|
-
return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, lookaheadCount)
|
|
46
|
+
private fun fromCpp(androidAutoEnabled: Boolean?, carPlayEnabled: Boolean?, showInNotification: Boolean?, lookaheadCount: Double?, androidNotificationIcon: String?): PlayerConfig {
|
|
47
|
+
return PlayerConfig(androidAutoEnabled, carPlayEnabled, showInNotification, lookaheadCount, androidNotificationIcon)
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
}
|
|
@@ -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?) {
|
|
21
|
+
init(androidAutoEnabled: Bool?, carPlayEnabled: Bool?, showInNotification: Bool?, 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)
|
|
@@ -43,6 +43,12 @@ public extension PlayerConfig {
|
|
|
43
43
|
} else {
|
|
44
44
|
return .init()
|
|
45
45
|
}
|
|
46
|
+
}(), { () -> bridge.std__optional_std__string_ in
|
|
47
|
+
if let __unwrappedValue = androidNotificationIcon {
|
|
48
|
+
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
49
|
+
} else {
|
|
50
|
+
return .init()
|
|
51
|
+
}
|
|
46
52
|
}())
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -93,4 +99,16 @@ public extension PlayerConfig {
|
|
|
93
99
|
}
|
|
94
100
|
}()
|
|
95
101
|
}
|
|
102
|
+
|
|
103
|
+
@inline(__always)
|
|
104
|
+
var androidNotificationIcon: String? {
|
|
105
|
+
return { () -> String? in
|
|
106
|
+
if bridge.has_value_std__optional_std__string_(self.__androidNotificationIcon) {
|
|
107
|
+
let __unwrapped = bridge.get_std__optional_std__string_(self.__androidNotificationIcon)
|
|
108
|
+
return String(__unwrapped)
|
|
109
|
+
} else {
|
|
110
|
+
return nil
|
|
111
|
+
}
|
|
112
|
+
}()
|
|
113
|
+
}
|
|
96
114
|
}
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
#include <optional>
|
|
34
|
+
#include <string>
|
|
34
35
|
|
|
35
36
|
namespace margelo::nitro::nitroplayer {
|
|
36
37
|
|
|
@@ -43,10 +44,11 @@ namespace margelo::nitro::nitroplayer {
|
|
|
43
44
|
std::optional<bool> carPlayEnabled SWIFT_PRIVATE;
|
|
44
45
|
std::optional<bool> showInNotification SWIFT_PRIVATE;
|
|
45
46
|
std::optional<double> lookaheadCount SWIFT_PRIVATE;
|
|
47
|
+
std::optional<std::string> androidNotificationIcon SWIFT_PRIVATE;
|
|
46
48
|
|
|
47
49
|
public:
|
|
48
50
|
PlayerConfig() = default;
|
|
49
|
-
explicit PlayerConfig(std::optional<bool> androidAutoEnabled, std::optional<bool> carPlayEnabled, std::optional<bool> showInNotification, std::optional<double> lookaheadCount): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), lookaheadCount(lookaheadCount) {}
|
|
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) {}
|
|
50
52
|
|
|
51
53
|
public:
|
|
52
54
|
friend bool operator==(const PlayerConfig& lhs, const PlayerConfig& rhs) = default;
|
|
@@ -65,7 +67,8 @@ namespace margelo::nitro {
|
|
|
65
67
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidAutoEnabled"))),
|
|
66
68
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled"))),
|
|
67
69
|
JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"))),
|
|
68
|
-
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount")))
|
|
70
|
+
JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"))),
|
|
71
|
+
JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))
|
|
69
72
|
);
|
|
70
73
|
}
|
|
71
74
|
static inline jsi::Value toJSI(jsi::Runtime& runtime, const margelo::nitro::nitroplayer::PlayerConfig& arg) {
|
|
@@ -74,6 +77,7 @@ namespace margelo::nitro {
|
|
|
74
77
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.carPlayEnabled));
|
|
75
78
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.showInNotification));
|
|
76
79
|
obj.setProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.lookaheadCount));
|
|
80
|
+
obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.androidNotificationIcon));
|
|
77
81
|
return obj;
|
|
78
82
|
}
|
|
79
83
|
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
@@ -88,6 +92,7 @@ namespace margelo::nitro {
|
|
|
88
92
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "carPlayEnabled")))) return false;
|
|
89
93
|
if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification")))) return false;
|
|
90
94
|
if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount")))) return false;
|
|
95
|
+
if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))) return false;
|
|
91
96
|
return true;
|
|
92
97
|
}
|
|
93
98
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-player",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|