Add Genre sorting & better placeholders

* Add Genre sorting to MusicSorter.

* Revamp placeholders so that theyre assigned when loading instead of using a BindingAdapter.
This commit is contained in:
OxygenCobalt 2020-08-31 16:10:32 -06:00
parent d158fc5786
commit 736e335ccf
11 changed files with 82 additions and 66 deletions

View file

@ -2,8 +2,10 @@ package org.oxycblt.auxio.music
import android.app.Application import android.app.Application
import android.util.Log import android.util.Log
import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.models.Album import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Genre
import org.oxycblt.auxio.music.models.Song import org.oxycblt.auxio.music.models.Song
import org.oxycblt.auxio.music.processing.MusicLoader import org.oxycblt.auxio.music.processing.MusicLoader
import org.oxycblt.auxio.music.processing.MusicLoaderResponse import org.oxycblt.auxio.music.processing.MusicLoaderResponse
@ -12,6 +14,7 @@ import org.oxycblt.auxio.music.processing.MusicSorter
// Storage for music data. // Storage for music data.
class MusicRepository { class MusicRepository {
lateinit var genres: List<Genre>
lateinit var artists: List<Artist> lateinit var artists: List<Artist>
lateinit var albums: List<Album> lateinit var albums: List<Album>
lateinit var songs: List<Song> lateinit var songs: List<Song>
@ -26,14 +29,20 @@ class MusicRepository {
if (loader.response == MusicLoaderResponse.DONE) { if (loader.response == MusicLoaderResponse.DONE) {
// If the loading succeeds, then process the songs and set them. // If the loading succeeds, then process the songs and set them.
val sorter = MusicSorter( val sorter = MusicSorter(
loader.genres,
loader.artists, loader.artists,
loader.albums, loader.albums,
loader.songs loader.songs,
app.applicationContext.getString(R.string.label_unknown_genre),
app.applicationContext.getString(R.string.label_unknown_artist),
app.applicationContext.getString(R.string.label_unknown_album)
) )
songs = sorter.songs.toList() songs = sorter.songs.toList()
albums = sorter.albums.toList() albums = sorter.albums.toList()
artists = sorter.artists.toList() artists = sorter.artists.toList()
genres = sorter.genres.toList()
val elapsed = System.currentTimeMillis() - start val elapsed = System.currentTimeMillis() - start

View file

@ -92,19 +92,3 @@ fun TextView.getAlbumSongs(album: Album) {
context.getString(R.string.format_multi_song_count, album.numSongs.toString()) context.getString(R.string.format_multi_song_count, album.numSongs.toString())
} }
} }
@BindingAdapter("songInfo")
fun TextView.getSongInfo(song: Song) {
var artist = song.album.artist.name
var album = song.album.title
if (artist == "") {
artist = context.getString(R.string.label_unknown_artist)
}
if (album == "") {
album = context.getString(R.string.label_unknown_album)
}
text = context.getString(R.string.format_song_info, artist, album)
}

View file

@ -5,8 +5,8 @@ import android.net.Uri
// Abstraction for Song // Abstraction for Song
data class Album( data class Album(
val id: Long = 0L, val id: Long = 0L,
val title: String = "", var title: String = "",
val artistName: String = "", val artistName: String = "", // Only used for sorting. Use artist for everything else.
val coverUri: Uri = Uri.EMPTY, val coverUri: Uri = Uri.EMPTY,
val year: Int = 0, val year: Int = 0,
var numSongs: Int = 0 var numSongs: Int = 0

View file

@ -3,8 +3,8 @@ package org.oxycblt.auxio.music.models
// Abstraction for mAlbums // Abstraction for mAlbums
data class Artist( data class Artist(
val id: Long = 0, val id: Long = 0,
val name: String = "", var name: String = "",
val genres: MutableList<String> = mutableListOf("") val genres: MutableList<Genre> = mutableListOf(Genre())
) { ) {
val albums = mutableListOf<Album>() val albums = mutableListOf<Album>()
var numAlbums = 0 var numAlbums = 0

View file

@ -1,6 +1,9 @@
package org.oxycblt.auxio.music.models package org.oxycblt.auxio.music.models
data class Genre( data class Genre(
val id: Long, val id: Long = 0,
val name: String var name: String = "",
) ) {
val artists = mutableListOf<Artist>()
var numArtists = 0
}

View file

@ -5,8 +5,8 @@ import android.text.format.DateUtils
// Class containing all relevant values for a song. // Class containing all relevant values for a song.
data class Song( data class Song(
val id: Long, val id: Long,
val title: String, var title: String,
val albumName: String, val albumName: String, // Only used for sorting. Use artist for everything else.
val track: Int, val track: Int,
val duration: Long val duration: Long
) { ) {

View file

@ -130,12 +130,12 @@ class MusicLoader(private val resolver: ContentResolver) {
val existingArtist = artists.find { it.name == name } val existingArtist = artists.find { it.name == name }
if (existingArtist != null) { if (existingArtist != null) {
existingArtist.genres.add(genre.name) existingArtist.genres.add(genre)
} else { } else {
artists.add( artists.add(
Artist( Artist(
id, name, id, name,
mutableListOf(genre.name) mutableListOf(genre)
) )
) )
} }

View file

@ -3,16 +3,25 @@ package org.oxycblt.auxio.music.processing
import android.util.Log import android.util.Log
import org.oxycblt.auxio.music.models.Album import org.oxycblt.auxio.music.models.Album
import org.oxycblt.auxio.music.models.Artist import org.oxycblt.auxio.music.models.Artist
import org.oxycblt.auxio.music.models.Genre
import org.oxycblt.auxio.music.models.Song import org.oxycblt.auxio.music.models.Song
class MusicSorter( class MusicSorter(
val genres: MutableList<Genre>,
val artists: MutableList<Artist>, val artists: MutableList<Artist>,
val albums: MutableList<Album>, val albums: MutableList<Album>,
val songs: MutableList<Song> val songs: MutableList<Song>,
private val genrePlaceholder: String,
private val artistPlaceholder: String,
private val albumPlaceholder: String,
) { ) {
init { init {
sortSongsIntoAlbums() sortSongsIntoAlbums()
sortAlbumsIntoArtists() sortAlbumsIntoArtists()
sortArtistsIntoGenres()
addPlaceholders()
} }
private fun sortSongsIntoAlbums() { private fun sortSongsIntoAlbums() {
@ -88,10 +97,57 @@ class MusicSorter(
unknownArtist.numAlbums = albums.size unknownArtist.numAlbums = albums.size
artists.add(unknownArtist)
Log.d( Log.d(
this::class.simpleName, this::class.simpleName,
"${unknownAlbums.size} albums were placed into an unknown artist." "${unknownAlbums.size} albums were placed into an unknown artist."
) )
} }
} }
private fun sortArtistsIntoGenres() {
Log.d(this::class.simpleName, "Sorting artists into genres...")
val unknownArtists = artists.toMutableList()
for (genre in genres) {
// Find all artists that match the current genre
val genreArtists = artists.filter { artist ->
artist.genres.any {
it.name == genre.name
}
}
// Then add them to the genre, along with refreshing the amount of artists
genre.artists.addAll(genreArtists)
genre.numArtists = artists.size
unknownArtists.removeAll(genreArtists)
}
if (unknownArtists.size > 0) {
// Reuse an existing unknown genre if one is found
val unknownGenre = genres.find { it.name == "" } ?: Genre()
for (artist in unknownArtists) {
artist.genres.add(unknownGenre)
unknownGenre.artists.add(artist)
}
unknownGenre.numArtists = artists.size
Log.d(
this::class.simpleName,
"${unknownArtists.size} albums were placed into an unknown genre."
)
}
}
// Correct any empty names [""] with the proper placeholders [Unknown Album]
private fun addPlaceholders() {
genres.forEach { if (it.name == "") it.name = genrePlaceholder }
artists.forEach { if (it.name == "") it.name = artistPlaceholder }
albums.forEach { if (it.title == "") it.title = albumPlaceholder }
}
} }

View file

@ -11,14 +11,7 @@ class AlbumAdapter(private val data: List<Album>) : RecyclerView.Adapter<AlbumVi
override fun getItemCount(): Int = data.size override fun getItemCount(): Int = data.size
/*
private var time = 0
private var inflationCount = 0
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder {
// val then = System.currentTimeMillis()
val binding = AlbumItemBinding.inflate(LayoutInflater.from(parent.context)) val binding = AlbumItemBinding.inflate(LayoutInflater.from(parent.context))
// Force the item to *actually* be the screen width so ellipsizing can work. // Force the item to *actually* be the screen width so ellipsizing can work.
@ -26,18 +19,6 @@ class AlbumAdapter(private val data: List<Album>) : RecyclerView.Adapter<AlbumVi
RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT
) )
/*
time += (System.currentTimeMillis() - then).toInt()
inflationCount++
if (inflationCount == 10) {
Log.d(
this::class.simpleName,
"Initial inflation took ${time}ms"
)
}
*/
return AlbumViewHolder(binding) return AlbumViewHolder(binding)
} }

View file

@ -11,11 +11,6 @@ class SongAdapter(private val data: List<Song>) : RecyclerView.Adapter<SongViewH
override fun getItemCount(): Int = data.size override fun getItemCount(): Int = data.size
/*
private var time = 0
private var inflationCount = 0
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
val then = System.currentTimeMillis() val then = System.currentTimeMillis()
@ -26,18 +21,6 @@ class SongAdapter(private val data: List<Song>) : RecyclerView.Adapter<SongViewH
RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT
) )
/*
time += (System.currentTimeMillis() - then).toInt()
inflationCount++
if (inflationCount == 10) {
Log.d(
this::class.simpleName,
"Initial inflation took ${time}ms"
)
}
*/
return SongViewHolder(binding) return SongViewHolder(binding)
} }

View file

@ -52,7 +52,7 @@
android:maxLines="1" android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceListItemSecondary" android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:songInfo="@{song}" android:text="@{@string/format_song_info(song.album.artist.name, song.album.title)}"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/duration" app:layout_constraintEnd_toStartOf="@+id/duration"
app:layout_constraintStart_toEndOf="@+id/cover" app:layout_constraintStart_toEndOf="@+id/cover"