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.util.Log
|
||||
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.Genre
|
||||
import org.oxycblt.auxio.music.models.Song
|
||||
import org.oxycblt.auxio.music.processing.MusicLoader
|
||||
import org.oxycblt.auxio.music.processing.MusicLoaderResponse
|
||||
|
@ -12,6 +14,7 @@ import org.oxycblt.auxio.music.processing.MusicSorter
|
|||
// Storage for music data.
|
||||
class MusicRepository {
|
||||
|
||||
lateinit var genres: List<Genre>
|
||||
lateinit var artists: List<Artist>
|
||||
lateinit var albums: List<Album>
|
||||
lateinit var songs: List<Song>
|
||||
|
@ -26,14 +29,20 @@ class MusicRepository {
|
|||
if (loader.response == MusicLoaderResponse.DONE) {
|
||||
// If the loading succeeds, then process the songs and set them.
|
||||
val sorter = MusicSorter(
|
||||
loader.genres,
|
||||
loader.artists,
|
||||
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()
|
||||
albums = sorter.albums.toList()
|
||||
artists = sorter.artists.toList()
|
||||
genres = sorter.genres.toList()
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
data class Album(
|
||||
val id: Long = 0L,
|
||||
val title: String = "",
|
||||
val artistName: String = "",
|
||||
var title: String = "",
|
||||
val artistName: String = "", // Only used for sorting. Use artist for everything else.
|
||||
val coverUri: Uri = Uri.EMPTY,
|
||||
val year: Int = 0,
|
||||
var numSongs: Int = 0
|
||||
|
|
|
@ -3,8 +3,8 @@ package org.oxycblt.auxio.music.models
|
|||
// Abstraction for mAlbums
|
||||
data class Artist(
|
||||
val id: Long = 0,
|
||||
val name: String = "",
|
||||
val genres: MutableList<String> = mutableListOf("")
|
||||
var name: String = "",
|
||||
val genres: MutableList<Genre> = mutableListOf(Genre())
|
||||
) {
|
||||
val albums = mutableListOf<Album>()
|
||||
var numAlbums = 0
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package org.oxycblt.auxio.music.models
|
||||
|
||||
data class Genre(
|
||||
val id: Long,
|
||||
val name: String
|
||||
)
|
||||
val id: Long = 0,
|
||||
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.
|
||||
data class Song(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
val albumName: String,
|
||||
var title: String,
|
||||
val albumName: String, // Only used for sorting. Use artist for everything else.
|
||||
val track: Int,
|
||||
val duration: Long
|
||||
) {
|
||||
|
|
|
@ -130,12 +130,12 @@ class MusicLoader(private val resolver: ContentResolver) {
|
|||
val existingArtist = artists.find { it.name == name }
|
||||
|
||||
if (existingArtist != null) {
|
||||
existingArtist.genres.add(genre.name)
|
||||
existingArtist.genres.add(genre)
|
||||
} else {
|
||||
artists.add(
|
||||
Artist(
|
||||
id, name,
|
||||
mutableListOf(genre.name)
|
||||
mutableListOf(genre)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,16 +3,25 @@ package org.oxycblt.auxio.music.processing
|
|||
import android.util.Log
|
||||
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.music.models.Song
|
||||
|
||||
class MusicSorter(
|
||||
val genres: MutableList<Genre>,
|
||||
val artists: MutableList<Artist>,
|
||||
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 {
|
||||
sortSongsIntoAlbums()
|
||||
sortAlbumsIntoArtists()
|
||||
sortArtistsIntoGenres()
|
||||
|
||||
addPlaceholders()
|
||||
}
|
||||
|
||||
private fun sortSongsIntoAlbums() {
|
||||
|
@ -88,10 +97,57 @@ class MusicSorter(
|
|||
|
||||
unknownArtist.numAlbums = albums.size
|
||||
|
||||
artists.add(unknownArtist)
|
||||
|
||||
Log.d(
|
||||
this::class.simpleName,
|
||||
"${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
|
||||
|
||||
/*
|
||||
private var time = 0
|
||||
private var inflationCount = 0
|
||||
*/
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder {
|
||||
// val then = System.currentTimeMillis()
|
||||
|
||||
val binding = AlbumItemBinding.inflate(LayoutInflater.from(parent.context))
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
/*
|
||||
time += (System.currentTimeMillis() - then).toInt()
|
||||
inflationCount++
|
||||
|
||||
if (inflationCount == 10) {
|
||||
Log.d(
|
||||
this::class.simpleName,
|
||||
"Initial inflation took ${time}ms"
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
return AlbumViewHolder(binding)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,6 @@ class SongAdapter(private val data: List<Song>) : RecyclerView.Adapter<SongViewH
|
|||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
/*
|
||||
private var time = 0
|
||||
private var inflationCount = 0
|
||||
*/
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongViewHolder {
|
||||
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
|
||||
)
|
||||
|
||||
/*
|
||||
time += (System.currentTimeMillis() - then).toInt()
|
||||
inflationCount++
|
||||
|
||||
if (inflationCount == 10) {
|
||||
Log.d(
|
||||
this::class.simpleName,
|
||||
"Initial inflation took ${time}ms"
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
return SongViewHolder(binding)
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
android:maxLines="1"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
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_constraintEnd_toStartOf="@+id/duration"
|
||||
app:layout_constraintStart_toEndOf="@+id/cover"
|
||||
|
|
Loading…
Reference in a new issue