react-native-nitro-player 0.7.1-alpha.0 → 0.7.1-alpha.2

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 (39) hide show
  1. package/README.md +47 -46
  2. package/android/src/main/AndroidManifest.xml +14 -1
  3. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +5 -6
  4. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +68 -49
  5. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridDownloadManager.kt +67 -21
  6. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridEqualizer.kt +27 -5
  7. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +88 -49
  8. package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +40 -10
  9. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ExoPlayerCore.kt +70 -29
  10. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/ListenerRegistry.kt +4 -1
  11. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerAndroidAuto.kt +38 -32
  12. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +70 -65
  13. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerListener.kt +23 -12
  14. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerNotify.kt +16 -4
  15. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerPlayback.kt +101 -72
  16. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueue.kt +64 -30
  17. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerQueueBuild.kt +54 -28
  18. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerSetup.kt +4 -3
  19. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerTempQueue.kt +73 -62
  20. package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerUrlLoader.kt +51 -48
  21. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadDatabase.kt +3 -3
  22. package/android/src/main/java/com/margelo/nitro/nitroplayer/download/DownloadManagerCore.kt +12 -3
  23. package/android/src/main/java/com/margelo/nitro/nitroplayer/equalizer/EqualizerCore.kt +169 -98
  24. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +30 -178
  25. package/android/src/main/java/com/margelo/nitro/nitroplayer/media/PlaybackService.kt +40 -0
  26. package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +87 -85
  27. package/ios/core/TrackPlayerQueue.swift +27 -18
  28. package/ios/core/TrackPlayerQueueBuild.swift +16 -5
  29. package/ios/equalizer/EqualizerCore.swift +39 -34
  30. package/lib/hooks/useEqualizer.js +10 -5
  31. package/lib/specs/Equalizer.nitro.d.ts +1 -1
  32. package/lib/types/EqualizerTypes.d.ts +3 -3
  33. package/package.json +5 -5
  34. package/src/hooks/useEqualizer.ts +25 -17
  35. package/src/specs/AndroidAutoMediaLibrary.nitro.ts +3 -2
  36. package/src/specs/DownloadManager.nitro.ts +4 -2
  37. package/src/specs/Equalizer.nitro.ts +5 -3
  38. package/src/specs/TrackPlayer.nitro.ts +18 -6
  39. package/src/types/EqualizerTypes.ts +17 -13
package/README.md CHANGED
@@ -42,41 +42,46 @@ npm install react-native-nitro-modules
42
42
 
43
43
  ### TrackPlayer Methods
44
44
 
45
+ Command-style methods return **`Promise<void>`** (or another **`Promise`**) and **reject** on failure; **`getRepeatMode()`** and **`isAndroidAutoConnected()`** are synchronous reads.
46
+
45
47
  | Name | Platform | Description |
46
48
  | --------------------------- | -------- | ------------------------------------------------------------------- |
47
- | `play()` | Both | Resumes playback. |
48
- | `pause()` | Both | Pauses playback. |
49
+ | `play()` | Both | **Async**. Resumes playback. |
50
+ | `pause()` | Both | **Async**. Pauses playback. |
49
51
  | `playSong(id, playlistId?)` | Both | **Async**. Plays a specific song, optionally from a playlist. |
50
- | `skipToNext()` | Both | Skips to the next track in the queue. |
51
- | `skipToPrevious()` | Both | Skips to the previous track. |
52
- | `seek(position)` | Both | Seeks to a specific time position in seconds. |
52
+ | `skipToNext()` | Both | **Async**. Skips to the next track in the queue. |
53
+ | `skipToPrevious()` | Both | **Async**. Skips to the previous track. |
54
+ | `seek(position)` | Both | **Async**. Seeks to a specific time position in seconds. |
53
55
  | `setPlaybackSpeed(speed)` | Both | **Async**. Sets playback speed (e.g. 0.5x, 1x, 1.5x, 2x). |
54
56
  | `getPlaybackSpeed()` | Both | **Async**. Gets the current playback speed. |
55
- | `setVolume(0-100)` | Both | Sets playback volume (0-100). |
56
- | `setRepeatMode(mode)` | Both | Sets repeat mode (`off`, `track`, `Playlist`). |
57
+ | `setVolume(0-100)` | Both | **Async**. Sets playback volume (0-100). |
58
+ | `setRepeatMode(mode)` | Both | **Async**. Sets repeat mode (`off`, `track`, `Playlist`). |
59
+ | `getRepeatMode()` | Both | **Sync**. Current repeat mode. |
57
60
  | `addToUpNext(id)` | Both | **Async**. Adds a track to the "up next" queue (FIFO). |
58
61
  | `playNext(id)` | Both | **Async**. Adds a track to the "play next" stack (LIFO). |
59
- | `getActualQueue()` | Both | **Async**. Gets the full playback queue including temporary tracks. |
60
- | `getState()` | Both | **Async**. Gets the current player state immediately. |
61
- | `skipToIndex(index)` | Both | **Async**. Skips to a specific index in the actual queue. |
62
- | `configure(config)` | Both | Configures player settings (Android Auto, etc.). |
63
- | `isAndroidAutoConnected()` | Both | Checks if Android Auto is currently connected. |
62
+ | `getActualQueue()` | Both | **Async**. Full playback queue including temporary tracks. |
63
+ | `getState()` | Both | **Async**. Snapshot of player state. |
64
+ | `skipToIndex(index)` | Both | **Async**. Skips to an index in the actual queue (`Promise<boolean>`). |
65
+ | `configure(config)` | Both | **Async**. Player settings (Android Auto, CarPlay, notification). |
66
+ | `isAndroidAutoConnected()` | Both | **Sync**. Android Auto connection. |
64
67
 
65
68
  ### PlayerQueue Methods
66
69
 
70
+ Mutations return **`Promise<void>`** (or **`Promise<string>`** for `createPlaylist`). **`getPlaylist`** / **`getAllPlaylists`** / **`getCurrentPlaylistId`** are synchronous reads.
71
+
67
72
  | Name | Platform | Description |
68
73
  | --------------------------------------- | -------- | ------------------------------------------------------- |
69
- | `createPlaylist(name, ...)` | Both | Creates a new playlist. Returns ID. |
70
- | `deletePlaylist(id)` | Both | Deletes a playlist by ID. |
71
- | `updatePlaylist(id, ...)` | Both | Updates playlist metadata (name, description, artwork). |
74
+ | `createPlaylist(name, ...)` | Both | **Async**. Creates a playlist; resolves to playlist ID. |
75
+ | `deletePlaylist(id)` | Both | **Async**. Deletes a playlist by ID. |
76
+ | `updatePlaylist(id, ...)` | Both | **Async**. Updates metadata (name, description, artwork). |
72
77
  | `getPlaylist(id)` | Both | Gets a specific playlist object. |
73
78
  | `getAllPlaylists()` | Both | Gets all available playlists. |
74
- | `loadPlaylist(id)` | Both | Loads a playlist for playback. |
79
+ | `loadPlaylist(id)` | Both | **Async**. Loads a playlist for playback. |
75
80
  | `getCurrentPlaylistId()` | Both | Gets the ID of the currently playing playlist. |
76
- | `addTrackToPlaylist(pid, track)` | Both | Adds a track to a playlist. |
77
- | `addTracksToPlaylist(pid, tracks)` | Both | Adds multiple tracks to a playlist. |
78
- | `removeTrackFromPlaylist(pid, tid)` | Both | Removes a track from a playlist. |
79
- | `reorderTrackInPlaylist(pid, tid, idx)` | Both | Moves a track to a new position in the playlist. |
81
+ | `addTrackToPlaylist(pid, track)` | Both | **Async**. Adds a track to a playlist. |
82
+ | `addTracksToPlaylist(pid, tracks)` | Both | **Async**. Adds multiple tracks to a playlist. |
83
+ | `removeTrackFromPlaylist(pid, tid)` | Both | **Async**. Removes a track from a playlist. |
84
+ | `reorderTrackInPlaylist(pid, tid, idx)` | Both | **Async**. Moves a track to a new position. |
80
85
 
81
86
  ### Platform-Specific APIs
82
87
 
@@ -98,8 +103,10 @@ npm install react-native-nitro-modules
98
103
  | `pauseDownload(downloadId)` | Both | **Async**. Pauses an active download. |
99
104
  | `resumeDownload(downloadId)` | Both | **Async**. Resumes a paused download. |
100
105
  | `cancelDownload(downloadId)` | Both | **Async**. Cancels a download. |
101
- | `isTrackDownloaded(trackId)` | Both | Checks if a track is downloaded. |
102
- | `getAllDownloadedTracks()` | Both | Gets all downloaded tracks. |
106
+ | `isTrackDownloaded(trackId)` | Both | **Async**. Checks on-disk download state. |
107
+ | `getAllDownloadedTracks()` | Both | **Async**. Lists persisted downloaded tracks. |
108
+ | `getEffectiveUrl(track)` | Both | **Async**. Local or remote URL from preference + downloads. |
109
+ | `syncDownloads()` | Both | **Async**. Reconcile DB with files; returns cleanup count. |
103
110
  | `deleteDownloadedTrack(trackId)` | Both | **Async**. Deletes a downloaded track. |
104
111
  | `getStorageInfo()` | Both | **Async**. Gets download storage usage information. |
105
112
  | `setPlaybackSourcePreference(pref)` | Both | Sets playback source: `'auto'`, `'download'`, or `'network'`. |
@@ -109,6 +116,8 @@ npm install react-native-nitro-modules
109
116
 
110
117
  ## Quick Start
111
118
 
119
+ `TrackPlayer` / `PlayerQueue` commands return **Promises**. The snippets use `await`—run them inside an `async` function, or use `.then()` / `void` / `.catch()` as appropriate.
120
+
112
121
  ### 1. Configure the Player
113
122
 
114
123
  Configure the player before using it in your app:
@@ -116,7 +125,7 @@ Configure the player before using it in your app:
116
125
  ```typescript
117
126
  import { TrackPlayer } from 'react-native-nitro-player'
118
127
 
119
- TrackPlayer.configure({
128
+ await TrackPlayer.configure({
120
129
  androidAutoEnabled: true,
121
130
  carPlayEnabled: false,
122
131
  showInNotification: true,
@@ -147,15 +156,13 @@ const tracks: TrackItem[] = [
147
156
  },
148
157
  ]
149
158
 
150
- // Create a playlist
151
- const playlistId = PlayerQueue.createPlaylist(
159
+ const playlistId = await PlayerQueue.createPlaylist(
152
160
  'My Playlist',
153
161
  'Playlist description',
154
162
  'https://example.com/playlist-artwork.jpg'
155
163
  )
156
164
 
157
- // Add tracks to the playlist
158
- PlayerQueue.addTracksToPlaylist(playlistId, tracks)
165
+ await PlayerQueue.addTracksToPlaylist(playlistId, tracks)
159
166
  ```
160
167
 
161
168
  ### 3. Play Music
@@ -163,28 +170,23 @@ PlayerQueue.addTracksToPlaylist(playlistId, tracks)
163
170
  ```typescript
164
171
  import { TrackPlayer, PlayerQueue } from 'react-native-nitro-player'
165
172
 
166
- // Load and play a playlist
167
- PlayerQueue.loadPlaylist(playlistId)
173
+ await PlayerQueue.loadPlaylist(playlistId)
168
174
 
169
- // Or play a specific song
170
175
  await TrackPlayer.playSong('song-id', playlistId)
171
176
 
172
- // Basic controls
173
- TrackPlayer.play()
174
- TrackPlayer.pause()
175
- TrackPlayer.skipToNext()
176
- TrackPlayer.skipToPrevious()
177
- TrackPlayer.seek(30) // Seek to 30 seconds
177
+ await TrackPlayer.play()
178
+ await TrackPlayer.pause()
179
+ await TrackPlayer.skipToNext()
180
+ await TrackPlayer.skipToPrevious()
181
+ await TrackPlayer.seek(30)
178
182
 
179
- // Set repeat mode
180
- TrackPlayer.setRepeatMode('off') // No repeat
181
- TrackPlayer.setRepeatMode('Playlist') // Repeat entire playlist
182
- TrackPlayer.setRepeatMode('track') // Repeat current track
183
+ await TrackPlayer.setRepeatMode('off')
184
+ await TrackPlayer.setRepeatMode('Playlist')
185
+ await TrackPlayer.setRepeatMode('track')
183
186
 
184
- // Set volume (0-100)
185
- TrackPlayer.setVolume(50) // Set volume to 50%
186
- TrackPlayer.setVolume(0) // Mute
187
- TrackPlayer.setVolume(100) // Maximum volume
187
+ await TrackPlayer.setVolume(50)
188
+ await TrackPlayer.setVolume(0)
189
+ await TrackPlayer.setVolume(100)
188
190
 
189
191
  // Add temporary tracks to queue
190
192
  await TrackPlayer.addToUpNext('song-id') // Add to up-next queue (FIFO)
@@ -280,8 +282,7 @@ The actual playback order is:
280
282
  Temporary tracks are automatically cleared when:
281
283
 
282
284
  - `await TrackPlayer.playSong()` is called
283
- - `PlayerQueue.loadPlaylist()` is called
284
- - `TrackPlayer.playFromIndex()` is called
285
+ - `await PlayerQueue.loadPlaylist()` is called
285
286
 
286
287
  ### `skipToIndex(index: number): Promise<boolean>`
287
288
 
@@ -4,8 +4,21 @@
4
4
  <uses-permission android:name="android.permission.INTERNET" />
5
5
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
6
6
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
7
-
7
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
8
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
9
+
8
10
  <application>
11
+ <!-- PlaybackService keeps music alive when screen is locked / app backgrounded -->
12
+ <service
13
+ android:name="com.margelo.nitro.nitroplayer.media.NitroPlayerPlaybackService"
14
+ android:foregroundServiceType="mediaPlayback"
15
+ android:exported="true"
16
+ tools:node="merge">
17
+ <intent-filter>
18
+ <action android:name="androidx.media3.session.MediaSessionService" />
19
+ </intent-filter>
20
+ </service>
21
+
9
22
  <!-- WorkManager's SystemForegroundService for download notifications -->
10
23
  <service
11
24
  android:name="androidx.work.impl.foreground.SystemForegroundService"
@@ -14,14 +14,13 @@ class HybridAndroidAutoMediaLibrary : HybridAndroidAutoMediaLibrarySpec() {
14
14
  private val core: TrackPlayerCore
15
15
 
16
16
  init {
17
- val context = NitroModules.applicationContext
18
- ?: throw IllegalStateException("React Context is not initialized")
17
+ val context =
18
+ NitroModules.applicationContext
19
+ ?: throw IllegalStateException("React Context is not initialized")
19
20
  core = TrackPlayerCore.getInstance(context)
20
21
  }
21
22
 
22
- override fun setMediaLibrary(libraryJson: String): Promise<Unit> =
23
- Promise.async { core.setAndroidAutoMediaLibrary(libraryJson) }
23
+ override fun setMediaLibrary(libraryJson: String): Promise<Unit> = Promise.async { core.setAndroidAutoMediaLibrary(libraryJson) }
24
24
 
25
- override fun clearMediaLibrary(): Promise<Unit> =
26
- Promise.async { core.clearAndroidAutoMediaLibrary() }
25
+ override fun clearMediaLibrary(): Promise<Unit> = Promise.async { core.clearAndroidAutoMediaLibrary() }
27
26
  }
@@ -19,12 +19,16 @@ class HybridAudioDevices : HybridAudioDevicesSpec() {
19
19
  private val audioManager = applicationContext?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
20
20
 
21
21
  private val validCommunicationDeviceTypes: Set<Int> by lazy {
22
- val types = mutableSetOf(
23
- AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
24
- AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
25
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
26
- AudioDeviceInfo.TYPE_USB_HEADSET,
27
- )
22
+ val types =
23
+ mutableSetOf(
24
+ AudioDeviceInfo.TYPE_BUILTIN_EARPIECE,
25
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
26
+ AudioDeviceInfo.TYPE_WIRED_HEADSET,
27
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
28
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
29
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
30
+ AudioDeviceInfo.TYPE_USB_HEADSET,
31
+ )
28
32
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
29
33
  types.add(AudioDeviceInfo.TYPE_BLE_HEADSET)
30
34
  types.add(AudioDeviceInfo.TYPE_BLE_SPEAKER)
@@ -35,55 +39,70 @@ class HybridAudioDevices : HybridAudioDevicesSpec() {
35
39
  override fun getAudioDevices(): Array<TAudioDevice> {
36
40
  val devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
37
41
  val activeDevice: AudioDeviceInfo? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) audioManager.communicationDevice else null
38
- return devices.filter { validCommunicationDeviceTypes.contains(it.type) }.map { device ->
39
- TAudioDevice(
40
- id = device.id.toDouble(),
41
- name = device.productName?.toString() ?: getDeviceTypeName(device.type),
42
- type = device.type.toDouble(),
43
- isActive = device == activeDevice,
44
- )
45
- }.toTypedArray()
42
+ return devices
43
+ .filter { validCommunicationDeviceTypes.contains(it.type) }
44
+ .map { device ->
45
+ TAudioDevice(
46
+ id = device.id.toDouble(),
47
+ name = device.productName?.toString() ?: getDeviceTypeName(device.type),
48
+ type = device.type.toDouble(),
49
+ isActive = device == activeDevice,
50
+ )
51
+ }.toTypedArray()
46
52
  }
47
53
 
48
54
  /** v2: setAudioDevice now returns Promise<Unit> instead of Boolean */
49
- override fun setAudioDevice(deviceId: Double): Promise<Unit> = Promise.async {
50
- val device = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
51
- .firstOrNull { it.id == deviceId.toInt() }
52
- ?: throw IllegalArgumentException("Audio device $deviceId not found")
53
- if (!validCommunicationDeviceTypes.contains(device.type)) {
54
- throw IllegalArgumentException("Device type ${device.type} is not a valid communication device")
55
- }
56
- try {
57
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
58
- audioManager.setCommunicationDevice(device)
59
- } else {
60
- when (device.type) {
61
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> {
62
- audioManager.startBluetoothSco(); audioManager.isBluetoothScoOn = true
63
- }
64
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> audioManager.isSpeakerphoneOn = true
65
- AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> {
66
- audioManager.isSpeakerphoneOn = false; audioManager.isBluetoothScoOn = false
55
+ override fun setAudioDevice(deviceId: Double): Promise<Unit> =
56
+ Promise.async {
57
+ val device =
58
+ audioManager
59
+ .getDevices(AudioManager.GET_DEVICES_OUTPUTS)
60
+ .firstOrNull { it.id == deviceId.toInt() }
61
+ ?: throw IllegalArgumentException("Audio device $deviceId not found")
62
+ if (!validCommunicationDeviceTypes.contains(device.type)) {
63
+ throw IllegalArgumentException("Device type ${device.type} is not a valid communication device")
64
+ }
65
+ try {
66
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
67
+ audioManager.setCommunicationDevice(device)
68
+ } else {
69
+ when (device.type) {
70
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO, AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> {
71
+ audioManager.startBluetoothSco()
72
+ audioManager.isBluetoothScoOn = true
73
+ }
74
+
75
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> {
76
+ audioManager.isSpeakerphoneOn = true
77
+ }
78
+
79
+ AudioDeviceInfo.TYPE_WIRED_HEADSET, AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> {
80
+ audioManager.isSpeakerphoneOn = false
81
+ audioManager.isBluetoothScoOn = false
82
+ }
83
+
84
+ else -> {
85
+ throw IllegalArgumentException("Unsupported device type for pre-Android 12: ${device.type}")
86
+ }
67
87
  }
68
- else -> throw IllegalArgumentException("Unsupported device type for pre-Android 12: ${device.type}")
69
88
  }
89
+ } catch (e: Exception) {
90
+ NitroPlayerLogger.log("HybridAudioDevices", "Error setting audio device: ${e.message}")
91
+ throw e
70
92
  }
71
- } catch (e: Exception) {
72
- NitroPlayerLogger.log("HybridAudioDevices", "Error setting audio device: ${e.message}")
73
- throw e
74
93
  }
75
- }
76
94
 
77
- private fun getDeviceTypeName(type: Int): String = when (type) {
78
- AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> "Built-in Earpiece"
79
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> "Built-in Speaker"
80
- AudioDeviceInfo.TYPE_WIRED_HEADSET -> "Wired Headset"
81
- AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> "Wired Headphones"
82
- AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "Bluetooth SCO"
83
- AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> "Bluetooth"
84
- AudioDeviceInfo.TYPE_USB_HEADSET -> "USB Headset"
85
- 26 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Headset" else "Type 26"
86
- 27 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Speaker" else "Type 27"
87
- else -> "Type $type"
88
- }
95
+ private fun getDeviceTypeName(type: Int): String =
96
+ when (type) {
97
+ AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> "Built-in Earpiece"
98
+ AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> "Built-in Speaker"
99
+ AudioDeviceInfo.TYPE_WIRED_HEADSET -> "Wired Headset"
100
+ AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> "Wired Headphones"
101
+ AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> "Bluetooth SCO"
102
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP -> "Bluetooth"
103
+ AudioDeviceInfo.TYPE_USB_HEADSET -> "USB Headset"
104
+ 26 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Headset" else "Type 26"
105
+ 27 -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) "BLE Speaker" else "Type 27"
106
+ else -> "Type $type"
107
+ }
89
108
  }
@@ -15,77 +15,123 @@ class HybridDownloadManager : HybridDownloadManagerSpec() {
15
15
  private val core: DownloadManagerCore
16
16
 
17
17
  init {
18
- val context = NitroModules.applicationContext
19
- ?: throw IllegalStateException("React Context is not initialized")
18
+ val context =
19
+ NitroModules.applicationContext
20
+ ?: throw IllegalStateException("React Context is not initialized")
20
21
  core = DownloadManagerCore.getInstance(context)
21
22
  }
22
23
 
23
24
  // ── Configuration ─────────────────────────────────────────────────────────
24
25
  override fun configure(config: DownloadConfig) = core.configure(config)
26
+
25
27
  override fun getConfig(): DownloadConfig = core.getConfig()
26
28
 
27
29
  // ── Download operations ───────────────────────────────────────────────────
28
- override fun downloadTrack(track: TrackItem, playlistId: String?): Promise<String> = Promise.async { core.downloadTrack(track, playlistId) }
29
- override fun downloadPlaylist(playlistId: String, tracks: Array<TrackItem>): Promise<Array<String>> = Promise.async { core.downloadPlaylist(playlistId, tracks) }
30
+ override fun downloadTrack(
31
+ track: TrackItem,
32
+ playlistId: String?,
33
+ ): Promise<String> = Promise.async { core.downloadTrack(track, playlistId) }
34
+
35
+ override fun downloadPlaylist(
36
+ playlistId: String,
37
+ tracks: Array<TrackItem>,
38
+ ): Promise<Array<String>> = Promise.async { core.downloadPlaylist(playlistId, tracks) }
39
+
30
40
  override fun pauseDownload(downloadId: String): Promise<Unit> = Promise.async { core.pauseDownload(downloadId) }
41
+
31
42
  override fun resumeDownload(downloadId: String): Promise<Unit> = Promise.async { core.resumeDownload(downloadId) }
43
+
32
44
  override fun cancelDownload(downloadId: String): Promise<Unit> = Promise.async { core.cancelDownload(downloadId) }
45
+
33
46
  override fun retryDownload(downloadId: String): Promise<Unit> = Promise.async { core.retryDownload(downloadId) }
47
+
34
48
  override fun pauseAllDownloads(): Promise<Unit> = Promise.async { core.pauseAllDownloads() }
49
+
35
50
  override fun resumeAllDownloads(): Promise<Unit> = Promise.async { core.resumeAllDownloads() }
51
+
36
52
  override fun cancelAllDownloads(): Promise<Unit> = Promise.async { core.cancelAllDownloads() }
37
53
 
38
54
  // ── Download status (sync) ────────────────────────────────────────────────
39
55
  override fun getDownloadTask(downloadId: String): Variant_NullType_DownloadTask {
40
56
  val task = core.getDownloadTask(downloadId)
41
- return if (task != null) Variant_NullType_DownloadTask.create(task)
42
- else Variant_NullType_DownloadTask.create(NullType.NULL)
57
+ return if (task != null) {
58
+ Variant_NullType_DownloadTask.create(task)
59
+ } else {
60
+ Variant_NullType_DownloadTask.create(NullType.NULL)
61
+ }
43
62
  }
63
+
44
64
  override fun getActiveDownloads(): Array<DownloadTask> = core.getActiveDownloads()
65
+
45
66
  override fun getQueueStatus(): DownloadQueueStatus = core.getQueueStatus()
67
+
46
68
  override fun isDownloading(trackId: String): Boolean = core.isDownloading(trackId)
69
+
47
70
  override fun getDownloadState(trackId: String): DownloadState = core.getDownloadState(trackId)
48
71
 
49
72
  // ── Downloaded content queries (now async per spec) ───────────────────────
50
73
  override fun isTrackDownloaded(trackId: String): Promise<Boolean> = Promise.async { core.isTrackDownloaded(trackId) }
74
+
51
75
  override fun isPlaylistDownloaded(playlistId: String): Promise<Boolean> = Promise.async { core.isPlaylistDownloaded(playlistId) }
76
+
52
77
  override fun isPlaylistPartiallyDownloaded(playlistId: String): Promise<Boolean> = Promise.async { core.isPlaylistPartiallyDownloaded(playlistId) }
53
78
 
54
- override fun getDownloadedTrack(trackId: String): Promise<Variant_NullType_DownloadedTrack> = Promise.async {
55
- val track = core.getDownloadedTrack(trackId)
56
- if (track != null) Variant_NullType_DownloadedTrack.create(track)
57
- else Variant_NullType_DownloadedTrack.create(NullType.NULL)
58
- }
79
+ override fun getDownloadedTrack(trackId: String): Promise<Variant_NullType_DownloadedTrack> =
80
+ Promise.async {
81
+ val track = core.getDownloadedTrack(trackId)
82
+ if (track != null) {
83
+ Variant_NullType_DownloadedTrack.create(track)
84
+ } else {
85
+ Variant_NullType_DownloadedTrack.create(NullType.NULL)
86
+ }
87
+ }
88
+
59
89
  override fun getAllDownloadedTracks(): Promise<Array<DownloadedTrack>> = Promise.async { core.getAllDownloadedTracks() }
60
90
 
61
- override fun getDownloadedPlaylist(playlistId: String): Promise<Variant_NullType_DownloadedPlaylist> = Promise.async {
62
- val playlist = core.getDownloadedPlaylist(playlistId)
63
- if (playlist != null) Variant_NullType_DownloadedPlaylist.create(playlist)
64
- else Variant_NullType_DownloadedPlaylist.create(NullType.NULL)
65
- }
91
+ override fun getDownloadedPlaylist(playlistId: String): Promise<Variant_NullType_DownloadedPlaylist> =
92
+ Promise.async {
93
+ val playlist = core.getDownloadedPlaylist(playlistId)
94
+ if (playlist != null) {
95
+ Variant_NullType_DownloadedPlaylist.create(playlist)
96
+ } else {
97
+ Variant_NullType_DownloadedPlaylist.create(NullType.NULL)
98
+ }
99
+ }
100
+
66
101
  override fun getAllDownloadedPlaylists(): Promise<Array<DownloadedPlaylist>> = Promise.async { core.getAllDownloadedPlaylists() }
67
102
 
68
- override fun getLocalPath(trackId: String): Promise<Variant_NullType_String> = Promise.async {
69
- val path = core.getLocalPath(trackId)
70
- if (path != null) Variant_NullType_String.create(path)
71
- else Variant_NullType_String.create(NullType.NULL)
72
- }
103
+ override fun getLocalPath(trackId: String): Promise<Variant_NullType_String> =
104
+ Promise.async {
105
+ val path = core.getLocalPath(trackId)
106
+ if (path != null) {
107
+ Variant_NullType_String.create(path)
108
+ } else {
109
+ Variant_NullType_String.create(NullType.NULL)
110
+ }
111
+ }
73
112
 
74
113
  override fun syncDownloads(): Promise<Double> = Promise.async { core.syncDownloads().toDouble() }
114
+
75
115
  override fun getEffectiveUrl(track: TrackItem): Promise<String> = Promise.async { core.getEffectiveUrl(track) }
76
116
 
77
117
  // ── Deletion ──────────────────────────────────────────────────────────────
78
118
  override fun deleteDownloadedTrack(trackId: String): Promise<Unit> = Promise.async { core.deleteDownloadedTrack(trackId) }
119
+
79
120
  override fun deleteDownloadedPlaylist(playlistId: String): Promise<Unit> = Promise.async { core.deleteDownloadedPlaylist(playlistId) }
121
+
80
122
  override fun deleteAllDownloads(): Promise<Unit> = Promise.async { core.deleteAllDownloads() }
123
+
81
124
  override fun getStorageInfo(): Promise<DownloadStorageInfo> = Promise.async { core.getStorageInfo() }
82
125
 
83
126
  // ── Playback source ───────────────────────────────────────────────────────
84
127
  override fun setPlaybackSourcePreference(preference: PlaybackSource) = core.setPlaybackSourcePreference(preference)
128
+
85
129
  override fun getPlaybackSourcePreference(): PlaybackSource = core.getPlaybackSourcePreference()
86
130
 
87
131
  // ── Events ────────────────────────────────────────────────────────────────
88
132
  override fun onDownloadProgress(callback: (progress: DownloadProgress) -> Unit) = core.addProgressCallback(callback)
133
+
89
134
  override fun onDownloadStateChange(callback: (downloadId: String, trackId: String, state: DownloadState, error: DownloadError?) -> Unit) = core.addStateChangeCallback(callback)
135
+
90
136
  override fun onDownloadComplete(callback: (downloadedTrack: DownloadedTrack) -> Unit) = core.addCompleteCallback(callback)
91
137
  }
@@ -13,37 +13,59 @@ class HybridEqualizer : HybridEqualizerSpec() {
13
13
  private val core: EqualizerCore
14
14
 
15
15
  init {
16
- val context = NitroModules.applicationContext
17
- ?: throw IllegalStateException("React Context is not initialized")
16
+ val context =
17
+ NitroModules.applicationContext
18
+ ?: throw IllegalStateException("React Context is not initialized")
18
19
  core = EqualizerCore.getInstance(context)
19
20
  core.ensureInitialized()
20
21
  }
21
22
 
22
23
  // ── Sync reads ────────────────────────────────────────────────────────────
23
24
  override fun isEnabled(): Boolean = core.isEnabled()
25
+
24
26
  override fun getBandRange(): GainRange = core.getBandRange()
27
+
25
28
  override fun getPresets(): Array<EqualizerPreset> = core.getPresets()
29
+
26
30
  override fun getBuiltInPresets(): Array<EqualizerPreset> = core.getBuiltInPresets()
31
+
27
32
  override fun getCustomPresets(): Array<EqualizerPreset> = core.getCustomPresets()
33
+
28
34
  override fun getCurrentPresetName(): Variant_NullType_String {
29
35
  val name = core.getCurrentPresetName()
30
- return if (name != null) Variant_NullType_String.create(name)
31
- else Variant_NullType_String.create(NullType.NULL)
36
+ return if (name != null) {
37
+ Variant_NullType_String.create(name)
38
+ } else {
39
+ Variant_NullType_String.create(NullType.NULL)
40
+ }
32
41
  }
33
42
 
34
43
  // ── Async mutations (per v2 spec) ─────────────────────────────────────────
35
44
  override fun setEnabled(enabled: Boolean): Promise<Unit> = Promise.async { core.setEnabled(enabled) }
45
+
36
46
  override fun getBands(): Promise<Array<EqualizerBand>> = Promise.async { core.getBands() }
37
- override fun setBandGain(bandIndex: Double, gainDb: Double): Promise<Unit> = Promise.async { core.setBandGain(bandIndex.toInt(), gainDb) }
47
+
48
+ override fun setBandGain(
49
+ bandIndex: Double,
50
+ gainDb: Double,
51
+ ): Promise<Unit> = Promise.async { core.setBandGain(bandIndex.toInt(), gainDb) }
52
+
38
53
  override fun setAllBandGains(gains: DoubleArray): Promise<Unit> = Promise.async { core.setAllBandGains(gains) }
54
+
39
55
  override fun applyPreset(presetName: String): Promise<Unit> = Promise.async { core.applyPreset(presetName) }
56
+
40
57
  override fun saveCustomPreset(name: String): Promise<Unit> = Promise.async { core.saveCustomPreset(name) }
58
+
41
59
  override fun deleteCustomPreset(name: String): Promise<Unit> = Promise.async { core.deleteCustomPreset(name) }
60
+
42
61
  override fun getState(): Promise<EqualizerState> = Promise.async { core.getState() }
62
+
43
63
  override fun reset(): Promise<Unit> = Promise.async { core.reset() }
44
64
 
45
65
  // ── Events ────────────────────────────────────────────────────────────────
46
66
  override fun onEnabledChange(callback: (enabled: Boolean) -> Unit) = core.addOnEnabledChangeListener(callback)
67
+
47
68
  override fun onBandChange(callback: (bands: Array<EqualizerBand>) -> Unit) = core.addOnBandChangeListener(callback)
69
+
48
70
  override fun onPresetChange(callback: (presetName: Variant_NullType_String?) -> Unit) = core.addOnPresetChangeListener(callback)
49
71
  }