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:
parent
d158fc5786
commit
736e335ccf
11 changed files with 82 additions and 66 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue