How to implement playlist/album structure in the FirebaseMusicSource code below?

38 views Asked by At

I have been using the following funcitons such as fetchMediaData(), asMediaSource() and asMediaItems() for first fetching data for songs and liveRadios and converting into MediaMetadataCompat as metadata of media items, asMediaSource is making concatenating songs and liveRadios which helps play items in a sequence and asMedaItems() converts the songs and liveRadios into MediaBrowseCompat.MediaItems. I am fetching data from Firestore.

The thing I am struggling with is how to create Albums/Playlists structure where Albums are FLAG_BROWSABLE and its children are FLAG_PLAYABLE, so I can attach the Albums data model class with a recyclerView adapter through which I can browse the items for each album and for that I can use a data model class called AlbumsMediaItems which can also be attached to a recyclerView adapter to diplay the items/Children of the album.

Here is my code below for FirebaseMusicSorce()

Any guidance/help would help me tons! Thanks in advance. If you need code of any other other part relating to FirebaseMusicSource, please do reply.

import android.content.ContentValues.TAG
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import android.util.Log
import androidx.core.net.toUri
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.source.ConcatenatingMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject

class FirebaseMusicSource @Inject constructor(
    private val musicDatabase: MusicDatabase
) {

    var songs = emptyList<MediaMetadataCompat>()
    var liveRadios = emptyList<MediaMetadataCompat>()


    suspend fun fetchMediaData() = withContext(Dispatchers.IO) {
        Log.d(TAG, "Fetching media data...")
        state = State.STATE_INITIALIZING
        val allSongs = musicDatabase.getAllSongs()
        songs = allSongs.map { song ->
            MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.subtitle)
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, song.mediaId)
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, song.title)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, song.imageUrl)
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, song.songUrl)
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, song.imageUrl)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, song.subtitle)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, song.subtitle)
                .build()
        }
        val allLiveRadios = musicDatabase.getAllLiveRadios()
        liveRadios = allLiveRadios.map { liveRadio ->
            MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, liveRadio.subtitle)
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, liveRadio.mediaId)
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, liveRadio.title)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, liveRadio.title)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, liveRadio.imageUrl)
                .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, liveRadio.songUrl)
                .putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, liveRadio.imageUrl)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, liveRadio.subtitle)
                .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, liveRadio.subtitle)
                .build()

        }
        withContext(Dispatchers.Main) {
            state = State.STATE_INITIALIZED
        }
    }

    fun asMediaSource(dataSourceFactory: DefaultDataSourceFactory): Pair<ConcatenatingMediaSource,   ConcatenatingMediaSource> {
        Log.d(TAG, "Creating media source...")
        val songsConcatenatingMediaSource = ConcatenatingMediaSource()
        val liveRadiosConcatenatingMediaSource = ConcatenatingMediaSource()
        val albumsConcatenatingMediaSource = ConcatenatingMediaSource()

        songs.forEach { song ->
            val mediaItem: MediaItem = MediaItem.fromUri(
                song.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI).toUri()
            )
            val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(mediaItem)
            songsConcatenatingMediaSource.addMediaSource(mediaSource)
        }

        liveRadios.forEach { liveRadio ->
            val mediaItem: MediaItem = MediaItem.fromUri(
                liveRadio.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI).toUri()
            )
            val mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
                .createMediaSource(mediaItem)
            liveRadiosConcatenatingMediaSource.addMediaSource(mediaSource)
        }

        return Pair(songsConcatenatingMediaSource, liveRadiosConcatenatingMediaSource)
    }

    fun asMediaItems(): Pair<MutableList<MediaBrowserCompat.MediaItem>,           MutableList<MediaBrowserCompat.MediaItem>> {
        Log.d(TAG, "Creating media items...")
        val songMediaItems = songs.map { song ->
            val desc = MediaDescriptionCompat.Builder()
                .setMediaUri(song.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI).toUri())
                .setTitle(song.description.title)
                .setSubtitle(song.description.subtitle)
                .setMediaId(song.description.mediaId)
                .setIconUri(song.description.iconUri)
                .build()
            MediaBrowserCompat.MediaItem(desc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
        }.toMutableList()

        val liveRadioMediaItems = liveRadios.map { liveRadio ->
            val desc = MediaDescriptionCompat.Builder()
                .setMediaUri(
                    liveRadio.getString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI).toUri()
                )
                .setTitle(liveRadio.description.title)
                .setSubtitle(liveRadio.description.subtitle)
                .setMediaId(liveRadio.description.mediaId)
                .setIconUri(liveRadio.description.iconUri)
                .build()
            MediaBrowserCompat.MediaItem(desc, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
        }.toMutableList()

        return Pair(songMediaItems, liveRadioMediaItems)
    }

    private val onReadyListeners = mutableListOf<(Boolean) -> Unit>()

    private var state = State.STATE_CREATED
        set(value) {
            if (value == State.STATE_INITIALIZED || value == State.STATE_ERROR) {
                synchronized(onReadyListeners) {
                    field = value
                    onReadyListeners.forEach { listener ->
                        listener(state == State.STATE_INITIALIZED)
                    }
                }
            } else {
                field = value
            }
        }

    fun whenReady(action: (Boolean) -> Unit): Boolean {
        if (state == State.STATE_CREATED || state == State.STATE_INITIALIZING) {
            onReadyListeners += action
            return false
        } else {
            action(state == State.STATE_INITIALIZED)
            return true
        }
    }
}

enum class State {
    STATE_CREATED,
    STATE_INITIALIZING,
    STATE_INITIALIZED,
    STATE_ERROR
}



Fetching data from Firstore.

class MusicDatabase {

    private val firestore = FirebaseFirestore.getInstance()
    private val songCollection = firestore.collection(SONG_COLLECTION)
    private val liveRadioCollection = firestore.collection(LIVE_RADIO_COLLECTION)


    suspend fun getAllSongs(): List<Song> {
        return try {
            songCollection.get().await().toObjects(Song::class.java)
        } catch (e: Exception) {
            emptyList()
        }
    }

    suspend fun getAllLiveRadios(): List<LiveRadio> {
        return try {
            liveRadioCollection.get().await().toObjects(LiveRadio::class.java)
        } catch (e: Exception) {
            emptyList()
        }
    }
}
0

There are 0 answers