react-native-nitro-player 0.0.1
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/NitroPlayer.podspec +31 -0
- package/README.md +610 -0
- package/android/CMakeLists.txt +29 -0
- package/android/build.gradle +147 -0
- package/android/fix-prefab.gradle +51 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +7 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrary.kt +29 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridAudioDevices.kt +116 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridPlayerQueue.kt +167 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/HybridTrackPlayer.kt +93 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/NitroPlayerPackage.kt +21 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/connection/AndroidAutoConnectionDetector.kt +171 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/core/TrackPlayerCore.kt +639 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaBrowserService.kt +352 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibrary.kt +58 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryManager.kt +77 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaLibraryParser.kt +73 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/media/MediaSessionManager.kt +506 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/Playlist.kt +21 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/playlist/PlaylistManager.kt +454 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/queue/Queue.kt +94 -0
- package/android/src/main/java/com/margelo/nitro/nitroplayer/queue/QueueManager.kt +143 -0
- package/ios/HybridAudioRoutePicker.swift +53 -0
- package/ios/HybridTrackPlayer.swift +100 -0
- package/ios/core/TrackPlayerCore.swift +1040 -0
- package/ios/media/MediaSessionManager.swift +230 -0
- package/ios/playlist/PlaylistManager.swift +446 -0
- package/ios/playlist/PlaylistModel.swift +49 -0
- package/ios/queue/HybridPlayerQueue.swift +95 -0
- package/ios/queue/Queue.swift +126 -0
- package/ios/queue/QueueManager.swift +157 -0
- package/lib/hooks/index.d.ts +6 -0
- package/lib/hooks/index.js +6 -0
- package/lib/hooks/useAndroidAutoConnection.d.ts +13 -0
- package/lib/hooks/useAndroidAutoConnection.js +26 -0
- package/lib/hooks/useAudioDevices.d.ts +26 -0
- package/lib/hooks/useAudioDevices.js +55 -0
- package/lib/hooks/useOnChangeTrack.d.ts +9 -0
- package/lib/hooks/useOnChangeTrack.js +17 -0
- package/lib/hooks/useOnPlaybackProgressChange.d.ts +9 -0
- package/lib/hooks/useOnPlaybackProgressChange.js +19 -0
- package/lib/hooks/useOnPlaybackStateChange.d.ts +9 -0
- package/lib/hooks/useOnPlaybackStateChange.js +17 -0
- package/lib/hooks/useOnSeek.d.ts +8 -0
- package/lib/hooks/useOnSeek.js +17 -0
- package/lib/index.d.ts +14 -0
- package/lib/index.js +24 -0
- package/lib/specs/AndroidAutoMediaLibrary.nitro.d.ts +21 -0
- package/lib/specs/AndroidAutoMediaLibrary.nitro.js +1 -0
- package/lib/specs/AudioDevices.nitro.d.ts +24 -0
- package/lib/specs/AudioDevices.nitro.js +1 -0
- package/lib/specs/AudioRoutePicker.nitro.d.ts +10 -0
- package/lib/specs/AudioRoutePicker.nitro.js +1 -0
- package/lib/specs/TrackPlayer.nitro.d.ts +39 -0
- package/lib/specs/TrackPlayer.nitro.js +1 -0
- package/lib/types/AndroidAutoMediaLibrary.d.ts +44 -0
- package/lib/types/AndroidAutoMediaLibrary.js +1 -0
- package/lib/types/PlayerQueue.d.ts +32 -0
- package/lib/types/PlayerQueue.js +1 -0
- package/lib/utils/androidAutoMediaLibrary.d.ts +47 -0
- package/lib/utils/androidAutoMediaLibrary.js +62 -0
- package/nitro.json +31 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/NitroPlayer+autolinking.cmake +91 -0
- package/nitrogen/generated/android/NitroPlayer+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroPlayerOnLoad.cpp +88 -0
- package/nitrogen/generated/android/NitroPlayerOnLoad.hpp +25 -0
- package/nitrogen/generated/android/c++/JFunc_void_TrackItem_std__optional_Reason_.hpp +85 -0
- package/nitrogen/generated/android/c++/JFunc_void_TrackPlayerState_std__optional_Reason_.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_bool.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_double_double_std__optional_bool_.hpp +76 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string_Playlist_std__optional_QueueOperation_.hpp +88 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Playlist__std__optional_QueueOperation_.hpp +106 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.cpp +55 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutoMediaLibrarySpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridAudioDevicesSpec.cpp +70 -0
- package/nitrogen/generated/android/c++/JHybridAudioDevicesSpec.hpp +66 -0
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.cpp +143 -0
- package/nitrogen/generated/android/c++/JHybridPlayerQueueSpec.hpp +77 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.cpp +137 -0
- package/nitrogen/generated/android/c++/JHybridTrackPlayerSpec.hpp +78 -0
- package/nitrogen/generated/android/c++/JPlayerConfig.hpp +65 -0
- package/nitrogen/generated/android/c++/JPlayerState.hpp +87 -0
- package/nitrogen/generated/android/c++/JPlaylist.hpp +99 -0
- package/nitrogen/generated/android/c++/JQueueOperation.hpp +65 -0
- package/nitrogen/generated/android/c++/JReason.hpp +65 -0
- package/nitrogen/generated/android/c++/JTAudioDevice.hpp +69 -0
- package/nitrogen/generated/android/c++/JTrackItem.hpp +86 -0
- package/nitrogen/generated/android/c++/JTrackPlayerState.hpp +62 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_Playlist.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_Playlist.hpp +77 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_String.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_String.hpp +70 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_TrackItem.cpp +26 -0
- package/nitrogen/generated/android/c++/JVariant_NullType_TrackItem.hpp +74 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_TrackItem_std__optional_Reason_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_TrackPlayerState_std__optional_Reason_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_bool.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_double_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_double_double_std__optional_bool_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__string_Playlist_std__optional_QueueOperation_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Func_void_std__vector_Playlist__std__optional_QueueOperation_.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAndroidAutoMediaLibrarySpec.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridAudioDevicesSpec.kt +61 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridPlayerQueueSpec.kt +116 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/HybridTrackPlayerSpec.kt +134 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/NitroPlayerOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerConfig.kt +44 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/PlayerState.kt +53 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Playlist.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/QueueOperation.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Reason.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/TAudioDevice.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/TrackItem.kt +56 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/TrackPlayerState.kt +22 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Variant_NullType_Playlist.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Variant_NullType_String.kt +59 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitroplayer/Variant_NullType_TrackItem.kt +59 -0
- package/nitrogen/generated/ios/NitroPlayer+autolinking.rb +60 -0
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.cpp +123 -0
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Bridge.hpp +531 -0
- package/nitrogen/generated/ios/NitroPlayer-Swift-Cxx-Umbrella.hpp +80 -0
- package/nitrogen/generated/ios/NitroPlayerAutolinking.mm +49 -0
- package/nitrogen/generated/ios/NitroPlayerAutolinking.swift +55 -0
- package/nitrogen/generated/ios/c++/HybridAudioRoutePickerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridAudioRoutePickerSpecSwift.hpp +74 -0
- package/nitrogen/generated/ios/c++/HybridPlayerQueueSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridPlayerQueueSpecSwift.hpp +167 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridTrackPlayerSpecSwift.hpp +174 -0
- package/nitrogen/generated/ios/swift/Func_void_TrackItem_std__optional_Reason_.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_TrackPlayerState_std__optional_Reason_.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_double_double.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_double_double_std__optional_bool_.swift +54 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string_Playlist_std__optional_QueueOperation_.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__vector_Playlist__std__optional_QueueOperation_.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridAudioRoutePickerSpec.swift +56 -0
- package/nitrogen/generated/ios/swift/HybridAudioRoutePickerSpec_cxx.swift +130 -0
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec.swift +68 -0
- package/nitrogen/generated/ios/swift/HybridPlayerQueueSpec_cxx.swift +349 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec.swift +69 -0
- package/nitrogen/generated/ios/swift/HybridTrackPlayerSpec_cxx.swift +325 -0
- package/nitrogen/generated/ios/swift/PlayerConfig.swift +115 -0
- package/nitrogen/generated/ios/swift/PlayerState.swift +181 -0
- package/nitrogen/generated/ios/swift/Playlist.swift +182 -0
- package/nitrogen/generated/ios/swift/QueueOperation.swift +48 -0
- package/nitrogen/generated/ios/swift/Reason.swift +48 -0
- package/nitrogen/generated/ios/swift/TrackItem.swift +147 -0
- package/nitrogen/generated/ios/swift/TrackPlayerState.swift +44 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_Playlist.swift +18 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_String.swift +18 -0
- package/nitrogen/generated/ios/swift/Variant_NullType_TrackItem.swift +18 -0
- package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridAndroidAutoMediaLibrarySpec.hpp +63 -0
- package/nitrogen/generated/shared/c++/HybridAudioDevicesSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridAudioDevicesSpec.hpp +65 -0
- package/nitrogen/generated/shared/c++/HybridAudioRoutePickerSpec.cpp +21 -0
- package/nitrogen/generated/shared/c++/HybridAudioRoutePickerSpec.hpp +62 -0
- package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.cpp +33 -0
- package/nitrogen/generated/shared/c++/HybridPlayerQueueSpec.hpp +87 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.cpp +34 -0
- package/nitrogen/generated/shared/c++/HybridTrackPlayerSpec.hpp +91 -0
- package/nitrogen/generated/shared/c++/PlayerConfig.hpp +83 -0
- package/nitrogen/generated/shared/c++/PlayerState.hpp +103 -0
- package/nitrogen/generated/shared/c++/Playlist.hpp +97 -0
- package/nitrogen/generated/shared/c++/QueueOperation.hpp +84 -0
- package/nitrogen/generated/shared/c++/Reason.hpp +84 -0
- package/nitrogen/generated/shared/c++/TAudioDevice.hpp +87 -0
- package/nitrogen/generated/shared/c++/TrackItem.hpp +102 -0
- package/nitrogen/generated/shared/c++/TrackPlayerState.hpp +80 -0
- package/package.json +172 -0
- package/react-native.config.js +16 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useAndroidAutoConnection.ts +30 -0
- package/src/hooks/useAudioDevices.ts +64 -0
- package/src/hooks/useOnChangeTrack.ts +24 -0
- package/src/hooks/useOnPlaybackProgressChange.ts +30 -0
- package/src/hooks/useOnPlaybackStateChange.ts +24 -0
- package/src/hooks/useOnSeek.ts +25 -0
- package/src/index.ts +47 -0
- package/src/specs/AndroidAutoMediaLibrary.nitro.ts +22 -0
- package/src/specs/AudioDevices.nitro.ts +25 -0
- package/src/specs/AudioRoutePicker.nitro.ts +9 -0
- package/src/specs/TrackPlayer.nitro.ts +81 -0
- package/src/types/AndroidAutoMediaLibrary.ts +58 -0
- package/src/types/PlayerQueue.ts +38 -0
- package/src/utils/androidAutoMediaLibrary.ts +66 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroplayer.playlist
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.SharedPreferences
|
|
5
|
+
import com.margelo.nitro.core.NullType
|
|
6
|
+
import com.margelo.nitro.nitroplayer.QueueOperation
|
|
7
|
+
import com.margelo.nitro.nitroplayer.TrackItem
|
|
8
|
+
import com.margelo.nitro.nitroplayer.Variant_NullType_String
|
|
9
|
+
import com.margelo.nitro.nitroplayer.core.TrackPlayerCore
|
|
10
|
+
import com.margelo.nitro.nitroplayer.media.NitroPlayerMediaBrowserService
|
|
11
|
+
import org.json.JSONArray
|
|
12
|
+
import org.json.JSONObject
|
|
13
|
+
import java.util.UUID
|
|
14
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Manages multiple playlists using ExoPlayer's native playlist functionality
|
|
18
|
+
* Based on: https://developer.android.com/media/media3/exoplayer/playlists
|
|
19
|
+
*/
|
|
20
|
+
class PlaylistManager private constructor(
|
|
21
|
+
private val context: Context,
|
|
22
|
+
) {
|
|
23
|
+
private val playlists: MutableMap<String, Playlist> = mutableMapOf()
|
|
24
|
+
private val listeners = CopyOnWriteArrayList<(List<Playlist>, QueueOperation?) -> Unit>()
|
|
25
|
+
private val playlistListeners = mutableMapOf<String, CopyOnWriteArrayList<(Playlist, QueueOperation?) -> Unit>>()
|
|
26
|
+
private var currentPlaylistId: String? = null
|
|
27
|
+
|
|
28
|
+
private val sharedPreferences: SharedPreferences =
|
|
29
|
+
context.getSharedPreferences("NitroPlayerPlaylists", Context.MODE_PRIVATE)
|
|
30
|
+
|
|
31
|
+
companion object {
|
|
32
|
+
@Volatile
|
|
33
|
+
@Suppress("ktlint:standard:property-naming")
|
|
34
|
+
private var INSTANCE: PlaylistManager? = null
|
|
35
|
+
|
|
36
|
+
@JvmStatic
|
|
37
|
+
fun getInstance(context: Context): PlaylistManager =
|
|
38
|
+
INSTANCE ?: synchronized(this) {
|
|
39
|
+
INSTANCE ?: PlaylistManager(context.applicationContext).also { INSTANCE = it }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
init {
|
|
44
|
+
// Don't load from preferences on init - only load when Android Auto needs it
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a new playlist
|
|
49
|
+
*/
|
|
50
|
+
fun createPlaylist(
|
|
51
|
+
name: String,
|
|
52
|
+
description: String? = null,
|
|
53
|
+
artwork: String? = null,
|
|
54
|
+
): String {
|
|
55
|
+
val id = UUID.randomUUID().toString()
|
|
56
|
+
val playlist = Playlist(id, name, description, artwork)
|
|
57
|
+
|
|
58
|
+
synchronized(playlists) {
|
|
59
|
+
playlists[id] = playlist
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Only cache for Android Auto if connected
|
|
63
|
+
if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
|
|
64
|
+
savePlaylistsToPreferences()
|
|
65
|
+
}
|
|
66
|
+
notifyPlaylistsChanged(QueueOperation.ADD)
|
|
67
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
|
|
68
|
+
|
|
69
|
+
return id
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Delete a playlist
|
|
74
|
+
*/
|
|
75
|
+
fun deletePlaylist(playlistId: String): Boolean {
|
|
76
|
+
val removed =
|
|
77
|
+
synchronized(playlists) {
|
|
78
|
+
playlists.remove(playlistId)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (removed != null) {
|
|
82
|
+
if (currentPlaylistId == playlistId) {
|
|
83
|
+
currentPlaylistId = null
|
|
84
|
+
}
|
|
85
|
+
playlistListeners.remove(playlistId)
|
|
86
|
+
// Only cache for Android Auto if connected
|
|
87
|
+
if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
|
|
88
|
+
savePlaylistsToPreferences()
|
|
89
|
+
}
|
|
90
|
+
notifyPlaylistsChanged(QueueOperation.REMOVE)
|
|
91
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Update playlist metadata
|
|
100
|
+
*/
|
|
101
|
+
fun updatePlaylist(
|
|
102
|
+
playlistId: String,
|
|
103
|
+
name: String? = null,
|
|
104
|
+
description: String? = null,
|
|
105
|
+
artwork: String? = null,
|
|
106
|
+
): Boolean {
|
|
107
|
+
val playlist =
|
|
108
|
+
synchronized(playlists) {
|
|
109
|
+
playlists[playlistId]
|
|
110
|
+
} ?: return false
|
|
111
|
+
|
|
112
|
+
synchronized(playlists) {
|
|
113
|
+
playlists[playlistId] =
|
|
114
|
+
playlist.copy(
|
|
115
|
+
name = name ?: playlist.name,
|
|
116
|
+
description = description ?: playlist.description,
|
|
117
|
+
artwork = artwork ?: playlist.artwork,
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Only cache for Android Auto if connected
|
|
122
|
+
if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
|
|
123
|
+
savePlaylistsToPreferences()
|
|
124
|
+
}
|
|
125
|
+
notifyPlaylistChanged(playlistId, QueueOperation.UPDATE)
|
|
126
|
+
notifyPlaylistsChanged(QueueOperation.UPDATE)
|
|
127
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistsUpdated()
|
|
128
|
+
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get a playlist by ID
|
|
134
|
+
*/
|
|
135
|
+
fun getPlaylist(playlistId: String): Playlist? =
|
|
136
|
+
synchronized(playlists) {
|
|
137
|
+
playlists[playlistId]
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get all playlists
|
|
142
|
+
*/
|
|
143
|
+
fun getAllPlaylists(): List<Playlist> =
|
|
144
|
+
synchronized(playlists) {
|
|
145
|
+
playlists.values.toList()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Add a track to a playlist
|
|
150
|
+
*/
|
|
151
|
+
fun addTrackToPlaylist(
|
|
152
|
+
playlistId: String,
|
|
153
|
+
track: TrackItem,
|
|
154
|
+
index: Int? = null,
|
|
155
|
+
): Boolean {
|
|
156
|
+
val playlist =
|
|
157
|
+
synchronized(playlists) {
|
|
158
|
+
playlists[playlistId]
|
|
159
|
+
} ?: return false
|
|
160
|
+
|
|
161
|
+
synchronized(playlists) {
|
|
162
|
+
val tracks = playlist.tracks.toMutableList()
|
|
163
|
+
if (index != null && index >= 0 && index <= tracks.size) {
|
|
164
|
+
tracks.add(index, track)
|
|
165
|
+
} else {
|
|
166
|
+
tracks.add(track)
|
|
167
|
+
}
|
|
168
|
+
playlists[playlistId] = playlist.copy(tracks = tracks)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Only cache for Android Auto if connected
|
|
172
|
+
if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
|
|
173
|
+
savePlaylistsToPreferences()
|
|
174
|
+
}
|
|
175
|
+
notifyPlaylistChanged(playlistId, QueueOperation.ADD)
|
|
176
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
|
|
177
|
+
|
|
178
|
+
// Update ExoPlayer if this is the current playlist
|
|
179
|
+
if (currentPlaylistId == playlistId) {
|
|
180
|
+
TrackPlayerCore.getInstance(context)?.updatePlaylist(playlistId)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Add multiple tracks to a playlist at once
|
|
188
|
+
*/
|
|
189
|
+
fun addTracksToPlaylist(
|
|
190
|
+
playlistId: String,
|
|
191
|
+
tracks: List<TrackItem>,
|
|
192
|
+
index: Int? = null,
|
|
193
|
+
): Boolean {
|
|
194
|
+
val playlist =
|
|
195
|
+
synchronized(playlists) {
|
|
196
|
+
playlists[playlistId]
|
|
197
|
+
} ?: return false
|
|
198
|
+
|
|
199
|
+
synchronized(playlists) {
|
|
200
|
+
val currentTracks = playlist.tracks.toMutableList()
|
|
201
|
+
if (index != null && index >= 0 && index <= currentTracks.size) {
|
|
202
|
+
currentTracks.addAll(index, tracks)
|
|
203
|
+
} else {
|
|
204
|
+
currentTracks.addAll(tracks)
|
|
205
|
+
}
|
|
206
|
+
playlists[playlistId] = playlist.copy(tracks = currentTracks)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Only cache for Android Auto if connected
|
|
210
|
+
if (NitroPlayerMediaBrowserService.isAndroidAutoConnected) {
|
|
211
|
+
savePlaylistsToPreferences()
|
|
212
|
+
}
|
|
213
|
+
notifyPlaylistChanged(playlistId, QueueOperation.ADD)
|
|
214
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
|
|
215
|
+
|
|
216
|
+
// Update ExoPlayer if this is the current playlist
|
|
217
|
+
if (currentPlaylistId == playlistId) {
|
|
218
|
+
TrackPlayerCore.getInstance(context)?.updatePlaylist(playlistId)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return true
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Remove a track from a playlist
|
|
226
|
+
*/
|
|
227
|
+
fun removeTrackFromPlaylist(
|
|
228
|
+
playlistId: String,
|
|
229
|
+
trackId: String,
|
|
230
|
+
): Boolean {
|
|
231
|
+
val playlist =
|
|
232
|
+
synchronized(playlists) {
|
|
233
|
+
playlists[playlistId]
|
|
234
|
+
} ?: return false
|
|
235
|
+
|
|
236
|
+
val removed =
|
|
237
|
+
synchronized(playlists) {
|
|
238
|
+
val tracks = playlist.tracks.toMutableList()
|
|
239
|
+
val removed = tracks.removeAll { it.id == trackId }
|
|
240
|
+
if (removed) {
|
|
241
|
+
playlists[playlistId] = playlist.copy(tracks = tracks)
|
|
242
|
+
}
|
|
243
|
+
removed
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (removed) {
|
|
247
|
+
savePlaylistsToPreferences()
|
|
248
|
+
notifyPlaylistChanged(playlistId, QueueOperation.REMOVE)
|
|
249
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
|
|
250
|
+
|
|
251
|
+
// Update ExoPlayer if this is the current playlist
|
|
252
|
+
if (currentPlaylistId == playlistId) {
|
|
253
|
+
TrackPlayerCore.getInstance(context)?.updatePlaylist(playlistId)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return removed
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Reorder a track in a playlist
|
|
262
|
+
*/
|
|
263
|
+
fun reorderTrackInPlaylist(
|
|
264
|
+
playlistId: String,
|
|
265
|
+
trackId: String,
|
|
266
|
+
newIndex: Int,
|
|
267
|
+
): Boolean {
|
|
268
|
+
val playlist =
|
|
269
|
+
synchronized(playlists) {
|
|
270
|
+
playlists[playlistId]
|
|
271
|
+
} ?: return false
|
|
272
|
+
|
|
273
|
+
val tracks = playlist.tracks.toMutableList()
|
|
274
|
+
val oldIndex = tracks.indexOfFirst { it.id == trackId }
|
|
275
|
+
if (oldIndex < 0 || newIndex < 0 || newIndex >= tracks.size) {
|
|
276
|
+
return false
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
synchronized(playlists) {
|
|
280
|
+
val track = tracks.removeAt(oldIndex)
|
|
281
|
+
tracks.add(newIndex, track)
|
|
282
|
+
playlists[playlistId] = playlist.copy(tracks = tracks)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
savePlaylistsToPreferences()
|
|
286
|
+
notifyPlaylistChanged(playlistId, QueueOperation.UPDATE)
|
|
287
|
+
NitroPlayerMediaBrowserService.getInstance()?.onPlaylistUpdated(playlistId)
|
|
288
|
+
|
|
289
|
+
// Update ExoPlayer if this is the current playlist
|
|
290
|
+
if (currentPlaylistId == playlistId) {
|
|
291
|
+
TrackPlayerCore.getInstance(context)?.updatePlaylist(playlistId)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return true
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Load a playlist for playback (sets it as current)
|
|
299
|
+
*/
|
|
300
|
+
fun loadPlaylist(playlistId: String): Boolean {
|
|
301
|
+
val playlist =
|
|
302
|
+
synchronized(playlists) {
|
|
303
|
+
playlists[playlistId]
|
|
304
|
+
} ?: return false
|
|
305
|
+
|
|
306
|
+
currentPlaylistId = playlistId
|
|
307
|
+
TrackPlayerCore.getInstance(context)?.loadPlaylist(playlistId)
|
|
308
|
+
|
|
309
|
+
return true
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get the current playlist ID
|
|
314
|
+
*/
|
|
315
|
+
fun getCurrentPlaylistId(): String? = currentPlaylistId
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Get the current playlist
|
|
319
|
+
*/
|
|
320
|
+
fun getCurrentPlaylist(): Playlist? = currentPlaylistId?.let { synchronized(playlists) { playlists[it] } }
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Add a listener for playlist changes
|
|
324
|
+
*/
|
|
325
|
+
fun addPlaylistsChangeListener(listener: (List<Playlist>, QueueOperation?) -> Unit): () -> Unit {
|
|
326
|
+
listeners.add(listener)
|
|
327
|
+
return { listeners.remove(listener) }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Add a listener for a specific playlist changes
|
|
332
|
+
*/
|
|
333
|
+
fun addPlaylistChangeListener(
|
|
334
|
+
playlistId: String,
|
|
335
|
+
listener: (Playlist, QueueOperation?) -> Unit,
|
|
336
|
+
): () -> Unit {
|
|
337
|
+
val playlistListeners = playlistListeners.getOrPut(playlistId) { CopyOnWriteArrayList() }
|
|
338
|
+
playlistListeners.add(listener)
|
|
339
|
+
return { playlistListeners.remove(listener) }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private fun notifyPlaylistsChanged(operation: QueueOperation?) {
|
|
343
|
+
val allPlaylists =
|
|
344
|
+
synchronized(playlists) {
|
|
345
|
+
playlists.values.toList()
|
|
346
|
+
}
|
|
347
|
+
listeners.forEach { it(allPlaylists, operation) }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private fun notifyPlaylistChanged(
|
|
351
|
+
playlistId: String,
|
|
352
|
+
operation: QueueOperation?,
|
|
353
|
+
) {
|
|
354
|
+
val playlist =
|
|
355
|
+
synchronized(playlists) {
|
|
356
|
+
playlists[playlistId]
|
|
357
|
+
} ?: return
|
|
358
|
+
|
|
359
|
+
playlistListeners[playlistId]?.forEach { it(playlist, operation) }
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private fun savePlaylistsToPreferences() {
|
|
363
|
+
try {
|
|
364
|
+
val jsonArray = JSONArray()
|
|
365
|
+
synchronized(playlists) {
|
|
366
|
+
playlists.values.forEach { playlist ->
|
|
367
|
+
val jsonObject =
|
|
368
|
+
JSONObject().apply {
|
|
369
|
+
put("id", playlist.id)
|
|
370
|
+
put("name", playlist.name)
|
|
371
|
+
put("description", playlist.description ?: "")
|
|
372
|
+
put("artwork", playlist.artwork ?: "")
|
|
373
|
+
val tracksArray = JSONArray()
|
|
374
|
+
playlist.tracks.forEach { track ->
|
|
375
|
+
tracksArray.put(
|
|
376
|
+
JSONObject().apply {
|
|
377
|
+
put("id", track.id)
|
|
378
|
+
put("title", track.title)
|
|
379
|
+
put("artist", track.artist)
|
|
380
|
+
put("album", track.album)
|
|
381
|
+
put("duration", track.duration)
|
|
382
|
+
put("url", track.url)
|
|
383
|
+
track.artwork?.let { put("artwork", it) }
|
|
384
|
+
},
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
put("tracks", tracksArray)
|
|
388
|
+
}
|
|
389
|
+
jsonArray.put(jsonObject)
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
sharedPreferences
|
|
394
|
+
.edit()
|
|
395
|
+
.putString("playlists", jsonArray.toString())
|
|
396
|
+
.putString("currentPlaylistId", currentPlaylistId)
|
|
397
|
+
.apply()
|
|
398
|
+
} catch (e: Exception) {
|
|
399
|
+
e.printStackTrace()
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private fun loadPlaylistsFromPreferences() {
|
|
404
|
+
try {
|
|
405
|
+
val jsonString = sharedPreferences.getString("playlists", null)
|
|
406
|
+
if (jsonString != null) {
|
|
407
|
+
val jsonArray = JSONArray(jsonString)
|
|
408
|
+
synchronized(playlists) {
|
|
409
|
+
playlists.clear()
|
|
410
|
+
for (i in 0 until jsonArray.length()) {
|
|
411
|
+
val jsonObject = jsonArray.getJSONObject(i)
|
|
412
|
+
val tracks = mutableListOf<TrackItem>()
|
|
413
|
+
val tracksArray = jsonObject.getJSONArray("tracks")
|
|
414
|
+
for (j in 0 until tracksArray.length()) {
|
|
415
|
+
val trackObj = tracksArray.getJSONObject(j)
|
|
416
|
+
val artworkStr = trackObj.optString("artwork")
|
|
417
|
+
val artwork: Variant_NullType_String? =
|
|
418
|
+
if (!artworkStr.isNullOrEmpty()) {
|
|
419
|
+
Variant_NullType_String.create(artworkStr)
|
|
420
|
+
} else {
|
|
421
|
+
null
|
|
422
|
+
}
|
|
423
|
+
tracks.add(
|
|
424
|
+
TrackItem(
|
|
425
|
+
id = trackObj.getString("id"),
|
|
426
|
+
title = trackObj.getString("title"),
|
|
427
|
+
artist = trackObj.getString("artist"),
|
|
428
|
+
album = trackObj.getString("album"),
|
|
429
|
+
duration = trackObj.getDouble("duration"),
|
|
430
|
+
url = trackObj.getString("url"),
|
|
431
|
+
artwork = artwork,
|
|
432
|
+
),
|
|
433
|
+
)
|
|
434
|
+
}
|
|
435
|
+
val descriptionStr = jsonObject.optString("description")
|
|
436
|
+
val artworkStr = jsonObject.optString("artwork")
|
|
437
|
+
val playlist =
|
|
438
|
+
Playlist(
|
|
439
|
+
id = jsonObject.getString("id"),
|
|
440
|
+
name = jsonObject.getString("name"),
|
|
441
|
+
description = if (!descriptionStr.isNullOrEmpty()) descriptionStr else null,
|
|
442
|
+
artwork = if (!artworkStr.isNullOrEmpty()) artworkStr else null,
|
|
443
|
+
tracks = tracks,
|
|
444
|
+
)
|
|
445
|
+
playlists[playlist.id] = playlist
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
currentPlaylistId = sharedPreferences.getString("currentPlaylistId", null)
|
|
449
|
+
}
|
|
450
|
+
} catch (e: Exception) {
|
|
451
|
+
e.printStackTrace()
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroplayer.queue
|
|
2
|
+
|
|
3
|
+
import com.margelo.nitro.nitroplayer.TrackItem
|
|
4
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Queue class that manages a list of tracks.
|
|
8
|
+
* Thread-safe implementation using CopyOnWriteArrayList.
|
|
9
|
+
*/
|
|
10
|
+
class Queue {
|
|
11
|
+
private val tracks: MutableList<TrackItem> = CopyOnWriteArrayList()
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get all tracks in the queue
|
|
15
|
+
*/
|
|
16
|
+
fun getTracks(): List<TrackItem> = tracks.toList()
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get tracks as an array (for compatibility with existing API)
|
|
20
|
+
*/
|
|
21
|
+
fun getTracksArray(): Array<TrackItem> = tracks.toTypedArray()
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Add a single track to the queue
|
|
25
|
+
*/
|
|
26
|
+
fun addTrack(track: TrackItem) {
|
|
27
|
+
tracks.add(track)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Add a track at a specific index
|
|
32
|
+
*/
|
|
33
|
+
fun addTrackAtIndex(
|
|
34
|
+
track: TrackItem,
|
|
35
|
+
index: Int,
|
|
36
|
+
) {
|
|
37
|
+
if (index < 0 || index > tracks.size) {
|
|
38
|
+
tracks.add(track)
|
|
39
|
+
} else {
|
|
40
|
+
tracks.add(index, track)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load multiple tracks into the queue (replaces existing queue)
|
|
46
|
+
*/
|
|
47
|
+
fun loadTracks(newTracks: List<TrackItem>) {
|
|
48
|
+
tracks.clear()
|
|
49
|
+
tracks.addAll(newTracks)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Remove a track by ID
|
|
54
|
+
* @return true if track was found and removed, false otherwise
|
|
55
|
+
*/
|
|
56
|
+
fun removeTrack(id: String): Boolean = tracks.removeAll { it.id == id }
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Clear all tracks from the queue
|
|
60
|
+
*/
|
|
61
|
+
fun clear() {
|
|
62
|
+
tracks.clear()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get the size of the queue
|
|
67
|
+
*/
|
|
68
|
+
fun size(): Int = tracks.size
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if the queue is empty
|
|
72
|
+
*/
|
|
73
|
+
fun isEmpty(): Boolean = tracks.isEmpty()
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get a track by index
|
|
77
|
+
*/
|
|
78
|
+
fun getTrack(index: Int): TrackItem? =
|
|
79
|
+
if (index >= 0 && index < tracks.size) {
|
|
80
|
+
tracks[index]
|
|
81
|
+
} else {
|
|
82
|
+
null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get a track by ID
|
|
87
|
+
*/
|
|
88
|
+
fun getTrackById(id: String): TrackItem? = tracks.find { it.id == id }
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the index of a track by ID
|
|
92
|
+
*/
|
|
93
|
+
fun getTrackIndex(id: String): Int = tracks.indexOfFirst { it.id == id }
|
|
94
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
package com.margelo.nitro.nitroplayer.queue
|
|
2
|
+
|
|
3
|
+
import com.margelo.nitro.nitroplayer.QueueOperation
|
|
4
|
+
import com.margelo.nitro.nitroplayer.TrackItem
|
|
5
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* QueueManager is a singleton that manages the queue state across the app session.
|
|
9
|
+
* It provides thread-safe access to the queue and notifies listeners of queue changes.
|
|
10
|
+
*/
|
|
11
|
+
class QueueManager private constructor() {
|
|
12
|
+
private val queue = Queue()
|
|
13
|
+
private val listeners = CopyOnWriteArrayList<(List<TrackItem>, QueueOperation?) -> Unit>()
|
|
14
|
+
|
|
15
|
+
companion object {
|
|
16
|
+
@Volatile
|
|
17
|
+
@Suppress("ktlint:standard:property-naming")
|
|
18
|
+
private var INSTANCE: QueueManager? = null
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get the singleton instance of QueueManager
|
|
22
|
+
*/
|
|
23
|
+
@JvmStatic
|
|
24
|
+
fun getInstance(): QueueManager =
|
|
25
|
+
INSTANCE ?: synchronized(this) {
|
|
26
|
+
INSTANCE ?: QueueManager().also { INSTANCE = it }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get the current queue
|
|
32
|
+
*/
|
|
33
|
+
fun getQueue(): Queue = queue
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get all tracks in the queue
|
|
37
|
+
*/
|
|
38
|
+
fun getTracks(): List<TrackItem> = queue.getTracks()
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get tracks as an array
|
|
42
|
+
*/
|
|
43
|
+
fun getTracksArray(): Array<TrackItem> = queue.getTracksArray()
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load multiple tracks into the queue (replaces existing queue)
|
|
47
|
+
*/
|
|
48
|
+
fun loadQueue(tracks: Array<TrackItem>) {
|
|
49
|
+
queue.loadTracks(tracks.toList())
|
|
50
|
+
notifyListeners(QueueOperation.ADD)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Load a single track at a specific index
|
|
55
|
+
*/
|
|
56
|
+
fun loadSingleTrack(
|
|
57
|
+
track: TrackItem,
|
|
58
|
+
index: Double?,
|
|
59
|
+
) {
|
|
60
|
+
val insertIndex = index?.toInt()
|
|
61
|
+
if (insertIndex != null && insertIndex >= 0) {
|
|
62
|
+
queue.addTrackAtIndex(track, insertIndex)
|
|
63
|
+
} else {
|
|
64
|
+
queue.addTrack(track)
|
|
65
|
+
}
|
|
66
|
+
notifyListeners(QueueOperation.ADD)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Delete a track by ID
|
|
71
|
+
*/
|
|
72
|
+
fun deleteTrack(id: String) {
|
|
73
|
+
val removed = queue.removeTrack(id)
|
|
74
|
+
if (removed) {
|
|
75
|
+
notifyListeners(QueueOperation.REMOVE)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Clear all tracks from the queue
|
|
81
|
+
*/
|
|
82
|
+
fun clearQueue() {
|
|
83
|
+
queue.clear()
|
|
84
|
+
notifyListeners(QueueOperation.CLEAR)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Add a listener for queue changes
|
|
89
|
+
* @param listener Callback that receives (queue, operation)
|
|
90
|
+
* @return A function to remove the listener
|
|
91
|
+
*/
|
|
92
|
+
fun addQueueChangeListener(listener: (List<TrackItem>, QueueOperation?) -> Unit): () -> Unit {
|
|
93
|
+
listeners.add(listener)
|
|
94
|
+
return { listeners.remove(listener) }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Remove a queue change listener
|
|
99
|
+
*/
|
|
100
|
+
fun removeQueueChangeListener(listener: (List<TrackItem>, QueueOperation?) -> Unit) {
|
|
101
|
+
listeners.remove(listener)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Notify all listeners of queue changes
|
|
106
|
+
*/
|
|
107
|
+
private fun notifyListeners(operation: QueueOperation?) {
|
|
108
|
+
val currentTracks = queue.getTracks()
|
|
109
|
+
listeners.forEach { listener ->
|
|
110
|
+
try {
|
|
111
|
+
listener(currentTracks, operation)
|
|
112
|
+
} catch (e: Exception) {
|
|
113
|
+
// Log error but don't break other listeners
|
|
114
|
+
e.printStackTrace()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get queue size
|
|
121
|
+
*/
|
|
122
|
+
fun getQueueSize(): Int = queue.size()
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Check if queue is empty
|
|
126
|
+
*/
|
|
127
|
+
fun isQueueEmpty(): Boolean = queue.isEmpty()
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get a track by index
|
|
131
|
+
*/
|
|
132
|
+
fun getTrack(index: Int): TrackItem? = queue.getTrack(index)
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get a track by ID
|
|
136
|
+
*/
|
|
137
|
+
fun getTrackById(id: String): TrackItem? = queue.getTrackById(id)
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the index of a track by ID
|
|
141
|
+
*/
|
|
142
|
+
fun getTrackIndex(id: String): Int = queue.getTrackIndex(id)
|
|
143
|
+
}
|