Add DetailFragment for genres

Add a DetailFragment for genres.
This commit is contained in:
OxygenCobalt 2020-09-27 09:47:53 -06:00
parent 21626d8d74
commit 9ffc194c4d
16 changed files with 497 additions and 16 deletions

View file

@ -33,6 +33,10 @@ TODO:
/other/
- Create inherited adapter/viewholder so I dont have to repeat as much code
- ? Condense detail fragments into a single fragment ?
- Make data items inherit a single class
- Condense artist/album recyclerview items into single item
- Remove binding adapters
To be added:

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Genre
import org.oxycblt.auxio.recycler.SortMode
class DetailViewModel : ViewModel() {
@ -13,12 +14,16 @@ class DetailViewModel : ViewModel() {
private val mNavToParentArtist = MutableLiveData<Boolean>()
val navToParentArtist: LiveData<Boolean> get() = mNavToParentArtist
private val mGenreSortMode = MutableLiveData(SortMode.ALPHA_DOWN)
val genreSortMode: LiveData<SortMode> get() = mGenreSortMode
private val mArtistSortMode = MutableLiveData(SortMode.NUMERIC_DOWN)
val artistSortMode: LiveData<SortMode> get() = mArtistSortMode
private val mAlbumSortMode = MutableLiveData(SortMode.NUMERIC_DOWN)
val albumSortMode: LiveData<SortMode> get() = mAlbumSortMode
var currentGenre: Genre? = null
var currentArtist: Artist? = null
var currentAlbum: Album? = null
@ -30,6 +35,15 @@ class DetailViewModel : ViewModel() {
mNavToParentArtist.value = false
}
fun incrementGenreSortMode() {
mGenreSortMode.value = when (mGenreSortMode.value) {
SortMode.ALPHA_DOWN -> SortMode.ALPHA_UP
SortMode.ALPHA_UP -> SortMode.ALPHA_DOWN
else -> SortMode.ALPHA_DOWN
}
}
fun incrementArtistSortMode() {
mArtistSortMode.value = when (mArtistSortMode.value) {
SortMode.NUMERIC_DOWN -> SortMode.NUMERIC_UP

View file

@ -0,0 +1,120 @@
package org.oxycblt.auxio.detail
import android.content.res.ColorStateList
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.recycler.SortMode
import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.toColor
class GenreDetailFragment : Fragment() {
private val args: GenreDetailFragmentArgs by navArgs()
private val detailModel: DetailViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentGenreDetailBinding.inflate(inflater)
// If DetailViewModel isn't already storing the genre, get it from MusicViewModel
// using the ID given by the navigation arguments
if (detailModel.currentGenre == null) {
val musicModel: MusicViewModel by activityViewModels()
detailModel.currentGenre = musicModel.genres.value!!.find {
it.id == args.genreId
}!!
}
val albumAdapter = DetailArtistAdapter(
ClickListener {
navToArtist(it)
}
)
binding.lifecycleOwner = this
binding.detailModel = detailModel
binding.genre = detailModel.currentGenre!!
binding.albumRecycler.adapter = albumAdapter
binding.albumRecycler.applyDivider()
binding.albumRecycler.setHasFixedSize(true)
binding.toolbar.setNavigationOnClickListener {
findNavController().navigateUp()
}
detailModel.genreSortMode.observe(viewLifecycleOwner) { mode ->
// Update the current sort icon
binding.sortButton.setImageResource(mode.iconRes)
// Then update the sort mode of the album adapter.
albumAdapter.submitList(
detailModel.currentGenre!!.artists.sortedWith(
SortMode.artistSortComparators.getOrDefault(
mode,
// If any invalid value is given, just default to the normal sort order.
compareByDescending { it.name }
)
)
)
}
// Don't enable the sort button if there is only one artist [Or less]
if (detailModel.currentGenre!!.numArtists < 2) {
binding.sortButton.imageTintList = ColorStateList.valueOf(
R.color.inactive_color.toColor(requireContext())
)
binding.sortButton.isEnabled = false
}
Log.d(this::class.simpleName, "Fragment created.")
return binding.root
}
override fun onResume() {
super.onResume()
detailModel.isAlreadyNavigating = false
}
override fun onDestroy() {
super.onDestroy()
// Reset the stored artist so that the next instance of GenreDetailFragment
// will not read it.
detailModel.currentGenre = null
}
private fun navToArtist(artist: Artist) {
// Don't navigate if an item already has been selected.
if (!detailModel.isAlreadyNavigating) {
detailModel.isAlreadyNavigating = true
findNavController().navigate(
GenreDetailFragmentDirections.actionShowArtist(artist.id)
)
}
}
}

View file

@ -0,0 +1,54 @@
package org.oxycblt.auxio.detail.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
import org.oxycblt.auxio.databinding.ItemGenreArtistBinding
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.recycler.AlbumDiffCallback
import org.oxycblt.auxio.recycler.ArtistDiffCallback
import org.oxycblt.auxio.recycler.ClickListener
class DetailArtistAdapter(
private val listener: ClickListener<Artist>
) : ListAdapter<Artist, DetailArtistAdapter.ViewHolder>(ArtistDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
ItemGenreArtistBinding.inflate(LayoutInflater.from(parent.context))
)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}
// Generic ViewHolder for an album
inner class ViewHolder(
private val binding: ItemGenreArtistBinding
) : RecyclerView.ViewHolder(binding.root) {
init {
// Force the viewholder to *actually* be the screen width so ellipsizing can work.
binding.root.layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT
)
}
// Bind the view w/new data
fun bind(artist: Artist) {
binding.artist = artist
binding.root.setOnClickListener {
listener.onClick(artist)
}
// Force-update the layout so ellipsizing works.
binding.artistImage.requestLayout()
binding.executePendingBindings()
}
}
}

View file

@ -16,6 +16,7 @@ import org.oxycblt.auxio.library.adapters.GenreAdapter
import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Genre
import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.SHOW_ALBUMS
import org.oxycblt.auxio.theme.SHOW_ARTISTS
@ -55,7 +56,7 @@ class LibraryFragment : Fragment() {
SHOW_GENRES -> GenreAdapter(
musicModel.genres.value!!,
ClickListener {
Log.d(this::class.simpleName, it.name)
navToGenre(it)
}
)
@ -100,4 +101,16 @@ class LibraryFragment : Fragment() {
)
}
}
private fun navToGenre(genre: Genre) {
if (!libraryModel.isAlreadyNavigating) {
libraryModel.isAlreadyNavigating = true
findNavController().navigate(
MainFragmentDirections.actionShowGenre(
genre.id
)
)
}
}
}

View file

@ -78,8 +78,17 @@ fun ImageView.getGenreImage(genre: Genre) {
if (genre.numArtists >= 4) {
val uris = mutableListOf<Uri>()
// For each artist, get the nth album from them [if possible].
for (i in 0..3) {
uris.add(genre.artists[i].albums[0].coverUri)
val artist = genre.artists[i]
uris.add(
if (artist.albums.size > i) {
artist.albums[i].coverUri
} else {
artist.albums[0].coverUri
}
)
}
val fetcher = MosaicFetcher(context)

View file

@ -5,6 +5,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.net.Uri
import android.util.Log
import androidx.core.graphics.drawable.toDrawable
import coil.bitmap.BitmapPool
import coil.decode.DataSource
@ -17,8 +18,11 @@ import coil.size.Size
import okio.buffer
import okio.source
import java.io.InputStream
import kotlin.math.pow
import kotlin.math.sqrt
const val MOSAIC_BITMAP_SIZE = 512
const val MOSAIC_BITMAP_INCREMENT = 256
class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
override suspend fun fetch(
@ -59,25 +63,24 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
var x = 0
var y = 0
val increment = MOSAIC_BITMAP_SIZE / 2
// For each stream, create a bitmap scaled to 1/4th of the mosaics combined size
// and place it on a corner of the canvas.
for (stream in streams) {
val bitmap = Bitmap.createScaledBitmap(
BitmapFactory.decodeStream(stream),
increment,
increment,
MOSAIC_BITMAP_INCREMENT,
MOSAIC_BITMAP_INCREMENT,
true
)
canvas.drawBitmap(bitmap, x.toFloat(), y.toFloat(), null)
x += increment
x += MOSAIC_BITMAP_INCREMENT
if (x == MOSAIC_BITMAP_SIZE) {
x = 0
y += increment
y += MOSAIC_BITMAP_INCREMENT
if (y == MOSAIC_BITMAP_SIZE) {
break

View file

@ -14,4 +14,11 @@ data class Genre(
}
return num
}
val numSongs: Int get() {
var num = 0
artists.forEach {
num += it.numSongs
}
return num
}
}

View file

@ -158,8 +158,7 @@ class MusicSorter(
// Finalize music
private fun finalizeMusic() {
// Remove genre duplicates now, as there's a risk duplicates could be added during the
// sorting process.
// Remove genre duplicates now, as duplicate genres can be added during the sorting process.
genres = genres.distinctBy {
it.name
}.toMutableList()

View file

@ -3,6 +3,7 @@ package org.oxycblt.auxio.recycler
import androidx.recyclerview.widget.DiffUtil
import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Song
// RecyclerView click listener
@ -30,6 +31,16 @@ class AlbumDiffCallback : DiffUtil.ItemCallback<Album>() {
}
}
class ArtistDiffCallback : DiffUtil.ItemCallback<Artist>() {
override fun areContentsTheSame(oldItem: Artist, newItem: Artist): Boolean {
return oldItem.id == newItem.id
}
override fun areItemsTheSame(oldItem: Artist, newItem: Artist): Boolean {
return oldItem == newItem
}
}
// Sorting modes
enum class SortMode(val iconRes: Int) {
// Icons for each mode are assigned to the enums themselves
@ -56,7 +67,17 @@ enum class SortMode(val iconRes: Int) {
) { it.name },
ALPHA_UP to compareBy(
String.CASE_INSENSITIVE_ORDER
) { it.name }
)
val artistSortComparators = mapOf<SortMode, Comparator<Artist>>(
// Alphabetic sorting needs to be case-insensitive
ALPHA_DOWN to compareBy(
String.CASE_INSENSITIVE_ORDER
) { it.name },
ALPHA_UP to compareByDescending(
String.CASE_INSENSITIVE_ORDER
) { it.name }
)
}
}

View file

@ -71,7 +71,7 @@
tools:text="Artist Name" />
<TextView
android:id="@+id/artist_genre"
android:id="@+id/genre_counts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
@ -83,7 +83,7 @@
tools:text="Genre Name" />
<TextView
android:id="@+id/artist_counts"
android:id="@+id/song_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
@ -91,7 +91,7 @@
android:layout_marginStart="@dimen/margin_medium"
app:artistCounts="@{artist}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_genre"
app:layout_constraintTop_toBottomOf="@+id/genre_counts"
tools:text="2 Albums, 20 Songs" />
<TextView
@ -109,7 +109,7 @@
android:textAppearance="@style/TextAppearance.MaterialComponents.Overline"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_counts" />
app:layout_constraintTop_toBottomOf="@+id/song_count" />
<ImageButton
android:id="@+id/sort_button"
@ -126,7 +126,7 @@
tools:src="@drawable/ic_sort_numeric_down"
app:layout_constraintBottom_toTopOf="@+id/album_recycler"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_counts" />
app:layout_constraintTop_toBottomOf="@+id/song_count" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/album_recycler"

View file

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="genre"
type="org.oxycblt.auxio.music.models.Genre" />
<variable
name="detailModel"
type="org.oxycblt.auxio.detail.DetailViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold"
app:navigationIcon="@drawable/ic_back"
app:title="@string/title_library_fragment" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
<ImageView
android:id="@+id/artist_image"
android:layout_width="@dimen/cover_size_huge"
android:layout_height="@dimen/cover_size_huge"
android:layout_marginTop="@dimen/margin_medium"
android:contentDescription="@{@string/description_genre_image(genre.name)}"
app:genreImage="@{genre}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_genre" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/artist_name"
style="@style/DetailHeader"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginStart="@dimen/margin_medium"
android:text="@{genre.name}"
app:autoSizeMaxTextSize="@dimen/detail_header_size_max"
app:autoSizeMinTextSize="@dimen/generic_size_min"
app:autoSizeStepGranularity="@dimen/generic_size_increment"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_image"
tools:text="Genre Name" />
<TextView
android:id="@+id/genre_counts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
android:layout_marginStart="@dimen/margin_medium"
app:genreCounts="@{genre}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_name"
tools:text="2 Artists, 4 Ablums" />
<TextView
android:id="@+id/song_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
android:layout_marginStart="@dimen/margin_medium"
android:text="@{@plurals/format_song_count(genre.numSongs, genre.numSongs)}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_counts"
tools:text="80 Songs" />
<TextView
android:id="@+id/header_title"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/header_dividers"
android:fontFamily="@font/inter_bold"
android:paddingStart="@dimen/padding_medium"
android:paddingTop="@dimen/padding_small"
android:paddingEnd="@dimen/padding_small"
android:paddingBottom="@dimen/padding_small"
android:text="@string/label_artists"
android:textAppearance="@style/TextAppearance.MaterialComponents.Overline"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/song_count" />
<ImageButton
android:id="@+id/sort_button"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/header_dividers"
android:contentDescription="@string/description_sort_button"
android:paddingStart="@dimen/padding_medium"
android:paddingTop="@dimen/padding_small"
android:paddingEnd="@dimen/margin_medium"
android:paddingBottom="@dimen/padding_small"
android:onClick="@{() -> detailModel.incrementGenreSortMode()}"
tools:src="@drawable/ic_sort_numeric_down"
app:layout_constraintBottom_toTopOf="@+id/album_recycler"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/song_count" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/album_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/header_title"
tools:itemCount="4"
tools:listitem="@layout/item_genre_artist" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
</layout>

View file

@ -20,8 +20,8 @@
<ImageView
android:id="@+id/cover"
android:layout_width="@dimen/cover_size_normal"
android:layout_height="@dimen/cover_size_normal"
android:layout_width="@dimen/cover_size_large"
android:layout_height="@dimen/cover_size_large"
android:contentDescription="@{@string/description_album_cover(album.name)}"
app:coverArt="@{album}"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="artist"
type="org.oxycblt.auxio.music.models.Artist" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple"
android:clickable="true"
android:focusable="true"
android:padding="@dimen/padding_medium">
<ImageView
android:id="@+id/artist_image"
android:layout_width="@dimen/cover_size_large"
android:layout_height="@dimen/cover_size_large"
android:contentDescription="@{@string/description_artist_image(artist.name)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:artistImage="@{artist}"
tools:src="@drawable/ic_artist" />
<TextView
android:id="@+id/artist_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@{artist.name}"
android:layout_marginStart="@dimen/margin_medium"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/album_song_count"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Artist Name" />
<TextView
android:id="@+id/album_song_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:layout_marginStart="@dimen/margin_medium"
android:textColor="?android:attr/textColorSecondary"
android:text="@{@plurals/format_album_count(artist.numAlbums, artist.numAlbums)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
app:layout_constraintTop_toBottomOf="@+id/artist_name"
tools:text="2 Albums" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -35,7 +35,18 @@
app:launchSingleTop="true" />
<action
android:id="@+id/action_show_album"
app:enterAnim="@anim/fragment_fade_enter"
app:exitAnim="@anim/fragment_fade_exit"
app:popEnterAnim="@anim/fragment_fade_enter"
app:popExitAnim="@anim/fragment_fade_exit"
app:destination="@id/album_detail_fragment" />
<action
android:id="@+id/action_show_genre"
app:enterAnim="@anim/fragment_fade_enter"
app:exitAnim="@anim/fragment_fade_exit"
app:popEnterAnim="@anim/fragment_fade_enter"
app:popExitAnim="@anim/fragment_fade_exit"
app:destination="@id/genreDetailFragment" />
</fragment>
<fragment
android:id="@+id/artist_detail_fragment"
@ -69,4 +80,19 @@
app:popExitAnim="@anim/fragment_fade_exit"
app:destination="@id/artist_detail_fragment" />
</fragment>
<fragment
android:id="@+id/genreDetailFragment"
android:name="org.oxycblt.auxio.detail.GenreDetailFragment"
android:label="GenreDetailFragment" >
<action
android:id="@+id/action_show_artist"
app:enterAnim="@anim/fragment_fade_enter"
app:exitAnim="@anim/fragment_fade_exit"
app:popEnterAnim="@anim/fragment_fade_enter"
app:popExitAnim="@anim/fragment_fade_exit"
app:destination="@id/artist_detail_fragment" />
<argument
android:name="genreId"
app:argType="long" />
</fragment>
</navigation>

View file

@ -11,6 +11,7 @@
<string name="label_retry">Retry</string>
<string name="label_grant">Grant</string>
<string name="label_artists">Artists</string>
<string name="label_albums">Albums</string>
<string name="label_songs">Songs</string>