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

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 (57) hide show
  1. package/android/src/main/AndroidManifest.xml +8 -0
  2. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +51 -0
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +2 -2
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +17 -2
  5. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/NitroPlayerLogger.kt +2 -2
  6. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +41 -31
  7. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCast.kt +1 -1
  8. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +47 -2
  9. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +10 -17
  10. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +5 -0
  11. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +3 -1
  12. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +16 -1
  13. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerRecovery.kt +97 -0
  14. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +0 -5
  15. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +15 -2
  16. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +3 -0
  17. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/TrackItemJson.kt +6 -0
  18. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/CastSessionController.kt +2 -0
  19. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaBrowserService.kt +96 -45
  20. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibrary.kt +2 -0
  21. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryManager.kt +147 -0
  22. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryParser.kt +3 -0
  23. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionCallbackFactory.kt +102 -24
  24. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +44 -2
  25. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +57 -1
  26. package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +15 -0
  27. package/ios/HybridTrackPlayer.swift +2 -1
  28. package/ios/core/TrackPlayerPlayback.swift +7 -3
  29. package/ios/media/MediaSessionManager.swift +8 -2
  30. package/lib/index.js +5 -1
  31. package/lib/specs/AndroidAutoMediaLibrary.nitro.d.ts +12 -0
  32. package/lib/types/AndroidAutoMediaLibrary.d.ts +11 -0
  33. package/lib/types/PlayerQueue.d.ts +4 -0
  34. package/lib/utils/androidAutoMediaLibrary.d.ts +13 -1
  35. package/lib/utils/androidAutoMediaLibrary.js +23 -0
  36. package/nitrogen/generated/android/NitroPlayerOnLoad.cpp +6 -0
  37. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  38. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  39. package/nitrogen/generated/android/c++/JFunc_void_std__string_std__string.hpp +76 -0
  40. package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.cpp +29 -0
  41. package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.hpp +6 -0
  42. package/nitrogen/generated/android/c++/JPlayerConfig.hpp +5 -1
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void.kt +80 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__string.kt +80 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__string_std__string.kt +80 -0
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrarySpec.kt +44 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +5 -2
  48. package/nitrogen/generated/ios/swift/PlayerConfig.swift +19 -1
  49. package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.cpp +6 -0
  50. package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.hpp +7 -0
  51. package/nitrogen/generated/shared/c++/PlayerConfig.hpp +5 -1
  52. package/package.json +1 -1
  53. package/src/index.ts +9 -1
  54. package/src/specs/AndroidAutoMediaLibrary.nitro.ts +18 -0
  55. package/src/types/AndroidAutoMediaLibrary.ts +24 -0
  56. package/src/types/PlayerQueue.ts +4 -0
  57. package/src/utils/androidAutoMediaLibrary.ts +48 -1
@@ -16,6 +16,12 @@ namespace margelo::nitro::nitroplayer {
16
16
  registerHybrids(this, [](Prototype& prototype) {
17
17
  prototype.registerHybridMethod("setMediaLibrary", &HybridAndroidAutoMediaLibrarySpec::setMediaLibrary);
18
18
  prototype.registerHybridMethod("clearMediaLibrary", &HybridAndroidAutoMediaLibrarySpec::clearMediaLibrary);
19
+ prototype.registerHybridMethod("onLoadChildren", &HybridAndroidAutoMediaLibrarySpec::onLoadChildren);
20
+ prototype.registerHybridMethod("onSearch", &HybridAndroidAutoMediaLibrarySpec::onSearch);
21
+ prototype.registerHybridMethod("resolveRequest", &HybridAndroidAutoMediaLibrarySpec::resolveRequest);
22
+ prototype.registerHybridMethod("setSessionButtons", &HybridAndroidAutoMediaLibrarySpec::setSessionButtons);
23
+ prototype.registerHybridMethod("onSessionButtonPress", &HybridAndroidAutoMediaLibrarySpec::onSessionButtonPress);
24
+ prototype.registerHybridMethod("onPlaybackResumption", &HybridAndroidAutoMediaLibrarySpec::onPlaybackResumption);
19
25
  });
20
26
  }
21
27
 
@@ -17,6 +17,7 @@
17
17
 
18
18
  #include <NitroModules/Promise.hpp>
19
19
  #include <string>
20
+ #include <functional>
20
21
 
21
22
  namespace margelo::nitro::nitroplayer {
22
23
 
@@ -51,6 +52,12 @@ namespace margelo::nitro::nitroplayer {
51
52
  // Methods
52
53
  virtual std::shared_ptr<Promise<void>> setMediaLibrary(const std::string& libraryJson) = 0;
53
54
  virtual std::shared_ptr<Promise<void>> clearMediaLibrary() = 0;
55
+ virtual void onLoadChildren(const std::function<void(const std::string& /* requestId */, const std::string& /* parentId */)>& callback) = 0;
56
+ virtual void onSearch(const std::function<void(const std::string& /* requestId */, const std::string& /* query */)>& callback) = 0;
57
+ virtual void resolveRequest(const std::string& requestId, const std::string& itemsJson) = 0;
58
+ virtual void setSessionButtons(const std::string& buttonsJson) = 0;
59
+ virtual void onSessionButtonPress(const std::function<void(const std::string& /* action */)>& callback) = 0;
60
+ virtual void onPlaybackResumption(const std::function<void()>& callback) = 0;
54
61
 
55
62
  protected:
56
63
  // Hybrid Setup
@@ -47,13 +47,14 @@ namespace margelo::nitro::nitroplayer {
47
47
  std::optional<bool> showInNotification SWIFT_PRIVATE;
48
48
  std::optional<double> remoteSkipForwardInterval SWIFT_PRIVATE;
49
49
  std::optional<double> remoteSkipBackwardInterval SWIFT_PRIVATE;
50
+ std::optional<bool> preferRemoteSkipIntervals SWIFT_PRIVATE;
50
51
  std::optional<double> lookaheadCount SWIFT_PRIVATE;
51
52
  std::optional<std::string> androidNotificationIcon SWIFT_PRIVATE;
52
53
  std::optional<AndroidAudioFocusMode> androidAudioFocus SWIFT_PRIVATE;
53
54
 
54
55
  public:
55
56
  PlayerConfig() = default;
56
- 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, std::optional<AndroidAudioFocusMode> androidAudioFocus): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), remoteSkipForwardInterval(remoteSkipForwardInterval), remoteSkipBackwardInterval(remoteSkipBackwardInterval), lookaheadCount(lookaheadCount), androidNotificationIcon(androidNotificationIcon), androidAudioFocus(androidAudioFocus) {}
57
+ explicit PlayerConfig(std::optional<bool> androidAutoEnabled, std::optional<bool> carPlayEnabled, std::optional<bool> showInNotification, std::optional<double> remoteSkipForwardInterval, std::optional<double> remoteSkipBackwardInterval, std::optional<bool> preferRemoteSkipIntervals, std::optional<double> lookaheadCount, std::optional<std::string> androidNotificationIcon, std::optional<AndroidAudioFocusMode> androidAudioFocus): androidAutoEnabled(androidAutoEnabled), carPlayEnabled(carPlayEnabled), showInNotification(showInNotification), remoteSkipForwardInterval(remoteSkipForwardInterval), remoteSkipBackwardInterval(remoteSkipBackwardInterval), preferRemoteSkipIntervals(preferRemoteSkipIntervals), lookaheadCount(lookaheadCount), androidNotificationIcon(androidNotificationIcon), androidAudioFocus(androidAudioFocus) {}
57
58
 
58
59
  public:
59
60
  friend bool operator==(const PlayerConfig& lhs, const PlayerConfig& rhs) = default;
@@ -74,6 +75,7 @@ namespace margelo::nitro {
74
75
  JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"))),
75
76
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipForwardInterval"))),
76
77
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipBackwardInterval"))),
78
+ JSIConverter<std::optional<bool>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "preferRemoteSkipIntervals"))),
77
79
  JSIConverter<std::optional<double>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"))),
78
80
  JSIConverter<std::optional<std::string>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon"))),
79
81
  JSIConverter<std::optional<margelo::nitro::nitroplayer::AndroidAudioFocusMode>>::fromJSI(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidAudioFocus")))
@@ -86,6 +88,7 @@ namespace margelo::nitro {
86
88
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "showInNotification"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.showInNotification));
87
89
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipForwardInterval"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.remoteSkipForwardInterval));
88
90
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipBackwardInterval"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.remoteSkipBackwardInterval));
91
+ obj.setProperty(runtime, PropNameIDCache::get(runtime, "preferRemoteSkipIntervals"), JSIConverter<std::optional<bool>>::toJSI(runtime, arg.preferRemoteSkipIntervals));
89
92
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount"), JSIConverter<std::optional<double>>::toJSI(runtime, arg.lookaheadCount));
90
93
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon"), JSIConverter<std::optional<std::string>>::toJSI(runtime, arg.androidNotificationIcon));
91
94
  obj.setProperty(runtime, PropNameIDCache::get(runtime, "androidAudioFocus"), JSIConverter<std::optional<margelo::nitro::nitroplayer::AndroidAudioFocusMode>>::toJSI(runtime, arg.androidAudioFocus));
@@ -104,6 +107,7 @@ namespace margelo::nitro {
104
107
  if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "showInNotification")))) return false;
105
108
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipForwardInterval")))) return false;
106
109
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "remoteSkipBackwardInterval")))) return false;
110
+ if (!JSIConverter<std::optional<bool>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "preferRemoteSkipIntervals")))) return false;
107
111
  if (!JSIConverter<std::optional<double>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "lookaheadCount")))) return false;
108
112
  if (!JSIConverter<std::optional<std::string>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidNotificationIcon")))) return false;
109
113
  if (!JSIConverter<std::optional<margelo::nitro::nitroplayer::AndroidAudioFocusMode>>::canConvert(runtime, obj.getProperty(runtime, PropNameIDCache::get(runtime, "androidAudioFocus")))) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-player",
3
- "version": "1.7.0-alpha.4",
3
+ "version": "1.7.0-alpha.6",
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",
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // TODO: Export all HybridObjects here for the user
2
2
 
3
3
  import { NitroModules } from 'react-native-nitro-modules'
4
- import { Platform } from 'react-native'
4
+ import { AppRegistry, Platform } from 'react-native'
5
5
  import type {
6
6
  PlayerQueue as PlayerQueueType,
7
7
  TrackPlayer as TrackPlayerType,
@@ -13,6 +13,14 @@ import type { Cast as CastType } from './specs/Cast.nitro'
13
13
  import type { DownloadManager as DownloadManagerType } from './specs/DownloadManager.nitro'
14
14
  import type { Equalizer as EqualizerType } from './specs/Equalizer.nitro'
15
15
 
16
+ // Native runs this while it needs JS without a resumed activity (car, screen off): it keeps JS timers ticking
17
+ if (Platform.OS === 'android') {
18
+ AppRegistry.registerHeadlessTask(
19
+ 'NitroPlayerKeepAlive',
20
+ () => () => new Promise<void>(() => {})
21
+ )
22
+ }
23
+
16
24
  export const PlayerQueue =
17
25
  NitroModules.createHybridObject<PlayerQueueType>('PlayerQueue')
18
26
  export const TrackPlayer =
@@ -20,4 +20,22 @@ export interface AndroidAutoMediaLibrary extends HybridObject<{
20
20
  * Falls back to showing all playlists
21
21
  */
22
22
  clearMediaLibrary(): Promise<void>
23
+
24
+ /** Android Auto opened a folder published with `children: []`; answer with {@link resolveRequest}. */
25
+ onLoadChildren(callback: (requestId: string, parentId: string) => void): void
26
+
27
+ /** A car search, or a voice request the loaded playlists cannot match; answer with {@link resolveRequest}. */
28
+ onSearch(callback: (requestId: string, query: string) => void): void
29
+
30
+ /** Answers {@link onLoadChildren} / {@link onSearch} with a JSON array of MediaItem. */
31
+ resolveRequest(requestId: string, itemsJson: string): void
32
+
33
+ /** Replaces the session buttons with a JSON array of SessionButton. */
34
+ setSessionButtons(buttonsJson: string): void
35
+
36
+ /** Called with the button's `action` when a session button is pressed. */
37
+ onSessionButtonPress(callback: (action: string) => void): void
38
+
39
+ /** A controller asked to play with nothing loaded (car auto-play, play on connect); load the last queue now. */
40
+ onPlaybackResumption(callback: () => void): void
23
41
  }
@@ -38,6 +38,9 @@ export interface MediaItem {
38
38
 
39
39
  /** Layout type for folder items (overrides library default) */
40
40
  layoutType?: LayoutType
41
+
42
+ /** Group header; contiguous items sharing one are listed under it */
43
+ groupTitle?: string
41
44
  }
42
45
 
43
46
  /**
@@ -56,3 +59,24 @@ export interface MediaLibrary {
56
59
  /** Optional app icon URL */
57
60
  appIconUrl?: string
58
61
  }
62
+
63
+ export type SessionButtonIcon =
64
+ | 'heart'
65
+ | 'heart_filled'
66
+ | 'star'
67
+ | 'star_filled'
68
+ | 'thumb_up'
69
+ | 'thumb_up_filled'
70
+ | 'plus'
71
+ | 'check'
72
+
73
+ /** App-defined button beside the transport controls in Android Auto and the notification */
74
+ export interface SessionButton {
75
+ /** Passed to the press callback */
76
+ action: string
77
+
78
+ /** Accessibility label */
79
+ title: string
80
+
81
+ icon: SessionButtonIcon
82
+ }
@@ -82,6 +82,10 @@ export interface PlayerConfig {
82
82
  * Fixed interval, in seconds, for remote skip-backward controls (default: 15)
83
83
  */
84
84
  remoteSkipBackwardInterval?: number
85
+ /**
86
+ * iOS only, ignored on Android: the lock screen, Control Center and CarPlay side buttons skip by the remote skip intervals instead of changing track (default: false)
87
+ */
88
+ preferRemoteSkipIntervals?: boolean
85
89
  /**
86
90
  * Number of upcoming tracks to preload URLs for (default: 5)
87
91
  * Higher values = more proactive loading, but more network requests
@@ -1,6 +1,24 @@
1
- import type { MediaLibrary } from '../types/AndroidAutoMediaLibrary'
1
+ import type {
2
+ MediaItem,
3
+ MediaLibrary,
4
+ SessionButton,
5
+ } from '../types/AndroidAutoMediaLibrary'
2
6
  import { AndroidAutoMediaLibrary as AndroidAutoMediaLibraryModule } from '../index'
3
7
 
8
+ type ItemsProvider = (
9
+ argument: string
10
+ ) => Promise<MediaItem[] | null | undefined>
11
+
12
+ const answer = (requestId: string, provide: () => ReturnType<ItemsProvider>) =>
13
+ provide()
14
+ .catch(() => null)
15
+ .then((items) =>
16
+ AndroidAutoMediaLibraryModule?.resolveRequest(
17
+ requestId,
18
+ JSON.stringify(items ?? [])
19
+ )
20
+ )
21
+
4
22
  /**
5
23
  * Helper utilities for Android Auto Media Library
6
24
  * Android-only functionality
@@ -57,6 +75,35 @@ export const AndroidAutoMediaLibraryHelper = {
57
75
  AndroidAutoMediaLibraryModule.clearMediaLibrary()
58
76
  },
59
77
 
78
+ /** 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. */
79
+ setChildrenLoader: (loader: ItemsProvider): void => {
80
+ AndroidAutoMediaLibraryModule?.onLoadChildren((requestId, parentId) =>
81
+ answer(requestId, () => loader(parentId))
82
+ )
83
+ },
84
+
85
+ /** Answers car searches and unmatched voice requests (first playable result plays); search shows from the next connection. */
86
+ setSearchHandler: (handler: ItemsProvider): void => {
87
+ AndroidAutoMediaLibraryModule?.onSearch((requestId, query) =>
88
+ answer(requestId, () => handler(query))
89
+ )
90
+ },
91
+
92
+ /** Buttons beside the transport controls in Android Auto and the notification; call again to change their state. */
93
+ setSessionButtons: (buttons: SessionButton[]): void => {
94
+ AndroidAutoMediaLibraryModule?.setSessionButtons(JSON.stringify(buttons))
95
+ },
96
+
97
+ /** Called with the pressed button's `action`. */
98
+ onSessionButtonPress: (callback: (action: string) => void): void => {
99
+ AndroidAutoMediaLibraryModule?.onSessionButtonPress(callback)
100
+ },
101
+
102
+ /** Car auto-play or play-on-connect found nothing loaded: restore your last queue (playback starts once it is loaded). */
103
+ onPlaybackResumption: (callback: () => void): void => {
104
+ AndroidAutoMediaLibraryModule?.onPlaybackResumption(callback)
105
+ },
106
+
60
107
  /**
61
108
  * Check if Android Auto Media Library is available (Android only)
62
109
  */