Update Music Loading

Slightly tweak the music models, use plural strings when formatting args, and properly sort the music items after everything else is sorted.
This commit is contained in:
OxygenCobalt 2020-09-06 18:51:01 -06:00
parent 565d1efa96
commit 016d664e51
16 changed files with 68 additions and 79 deletions

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
.gradle
local.properties
build/
release/
# Studio
.idea/

View file

@ -18,4 +18,6 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-dontobfuscate

View file

@ -11,6 +11,8 @@ import org.oxycblt.auxio.theme.accent
class MainActivity : AppCompatActivity() {
// TODO: Collapse LoadingFragment/MainFragment into MainActivity.
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
// Debugging placeholder
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

View file

@ -21,8 +21,6 @@ import org.oxycblt.auxio.music.processing.MusicLoaderResponse
class LoadingFragment : Fragment(R.layout.fragment_loading) {
// TODO: Phase out LoadingFragment
private val loadingModel: LoadingViewModel by lazy {
ViewModelProvider(
this,

View file

@ -3,6 +3,7 @@ package org.oxycblt.auxio.music
import android.content.ContentUris
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import android.widget.ImageView
import android.widget.TextView
import androidx.databinding.BindingAdapter
@ -65,57 +66,40 @@ fun Long.toAlbumArtURI(): Uri {
)
}
fun Int.toSongCount(): Int {
return if (this < 2) {
R.string.label_single_song
} else {
R.string.format_multi_song_count
}
}
fun Int.toAlbumCount(): Int {
return if (this < 2) {
R.string.label_single_album
} else {
R.string.format_album_count
}
}
// Format the amount of songs in an album
@BindingAdapter("songCount")
fun TextView.getAlbumSongs(album: Album) {
text = context.getString(album.numSongs.toSongCount(), album.numSongs)
text = context.resources.getQuantityString(R.plurals.format_song_count, album.numSongs)
}
@BindingAdapter("albumSongCount")
fun TextView.getSongAlbumCount(artist: Artist) {
val albums = context.getString(artist.numAlbums.toAlbumCount(), artist.numAlbums)
val songs = context.getString(artist.numSongs.toSongCount(), artist.numSongs)
@BindingAdapter("artistCounts")
fun TextView.getArtistCounts(artist: Artist) {
// Get the quantity string for both albums & artists, and then stitch them together.
val albums = context.resources.getQuantityString(
R.plurals.format_albums, artist.numAlbums, artist.numAlbums
)
val songs = context.resources.getQuantityString(
R.plurals.format_song_count, artist.numSongs, artist.numSongs
)
text = context.getString(R.string.format_combined_song_album, albums, songs)
Log.d("getArtistCounts", albums)
text = context.getString(R.string.format_double_counts, albums, songs)
}
// Get the cover art
@BindingAdapter("coverArt")
fun ImageView.getCoverArt(any: Any) {
val uri = when (any) {
is Song -> any.album.coverUri
is Album -> any.coverUri
else -> Uri.EMPTY
}
load(uri) {
fun ImageView.getCoverArt(song: Song) {
load(song.album.coverUri) {
crossfade(true)
placeholder(android.R.color.transparent)
error(R.drawable.ic_music)
}
}
// Get the artist image.
@BindingAdapter("artistImage")
fun ImageView.getArtistImage(artist: Artist) {
load(artist.albums[0].coverUri) {
@BindingAdapter("coverArt")
fun ImageView.getCoverArt(album: Album) {
load(album.coverUri) {
crossfade(true)
placeholder(android.R.color.transparent)
error(R.drawable.ic_music)

View file

@ -5,15 +5,15 @@ import android.net.Uri
// Abstraction for Song
data class Album(
val id: Long = 0L,
var title: String = "",
val artistName: String = "", // Only used for sorting. Use artist for everything else.
var name: String = "",
val artistName: String = "", // only used for sorting. Use artist.name instead.
val coverUri: Uri = Uri.EMPTY,
val year: Int = 0,
var numSongs: Int = 0
val year: Int = 0
) {
lateinit var artist: Artist
val songs = mutableListOf<Song>()
val numSongs: Int get() = songs.size
fun finalize() {
songs.sortBy { it.track }

View file

@ -7,13 +7,13 @@ data class Artist(
val genres: MutableList<Genre> = mutableListOf(Genre())
) {
val albums = mutableListOf<Album>()
var numAlbums = 0
val numAlbums: Int get() = albums.size
var numSongs = 0
fun finalize() {
albums.sortByDescending { it.year }
numAlbums = albums.size
albums.forEach { album ->
numSongs += album.numSongs
}

View file

@ -5,10 +5,8 @@ data class Genre(
var name: String = "",
) {
val artists = mutableListOf<Artist>()
var numArtists = 0
fun finalize() {
artists.sortByDescending { it.name }
numArtists = artists.size
}
}

View file

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

View file

@ -113,8 +113,8 @@ class MusicLoader(private val resolver: ContentResolver) {
artistCursor = resolver.query(
Genres.Members.getContentUri("external", genre.id),
arrayOf(
Artists._ID,
Artists.ARTIST
Artists._ID, // 0
Artists.ARTIST // 1
),
null, null,
Artists.DEFAULT_SORT_ORDER
@ -171,7 +171,6 @@ class MusicLoader(private val resolver: ContentResolver) {
Albums.ARTIST, // 2
Albums.FIRST_YEAR, // 3
Albums.NUMBER_OF_SONGS // 4
),
null, null,
Albums.DEFAULT_SORT_ORDER
@ -182,21 +181,19 @@ class MusicLoader(private val resolver: ContentResolver) {
val nameIndex = cursor.getColumnIndexOrThrow(Albums.ALBUM)
val artistIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST)
val yearIndex = cursor.getColumnIndexOrThrow(Albums.FIRST_YEAR)
val numIndex = cursor.getColumnIndexOrThrow(Albums.NUMBER_OF_SONGS)
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
val name = cursor.getString(nameIndex) ?: ""
val artist = cursor.getString(artistIndex) ?: ""
val year = cursor.getInt(yearIndex)
val numSongs = cursor.getInt(numIndex)
val coverUri = id.toAlbumArtURI()
albums.add(
Album(
id, name, artist,
coverUri, year, numSongs
coverUri, year
)
)
}
@ -206,7 +203,7 @@ class MusicLoader(private val resolver: ContentResolver) {
// Remove dupes
albums = albums.distinctBy {
it.title to it.artistName to it.year to it.numSongs
it.name to it.artistName to it.year to it.numSongs
}.toMutableList()
Log.d(
@ -260,7 +257,7 @@ class MusicLoader(private val resolver: ContentResolver) {
// Remove dupes
songs = songs.distinctBy {
it.title to it.albumName to it.track to it.duration
it.name to it.albumName to it.track to it.duration
}.toMutableList()
Log.d(

View file

@ -22,6 +22,7 @@ class MusicSorter(
sortArtistsIntoGenres()
addPlaceholders()
finalizeMusic()
}
private fun sortSongsIntoAlbums() {
@ -31,7 +32,7 @@ class MusicSorter(
for (album in albums) {
// Find all songs that match the current album title
val albumSongs = songs.filter { it.albumName == album.title }
val albumSongs = songs.filter { it.albumName == album.name }
// Then add them to the album
for (song in albumSongs) {
@ -46,14 +47,13 @@ class MusicSorter(
if (unknownSongs.size > 0) {
// Reuse an existing unknown album if one is found
val unknownAlbum = albums.find { it.title == "" } ?: Album()
val unknownAlbum = albums.find { it.name == "" } ?: Album()
for (song in unknownSongs) {
song.album = unknownAlbum
unknownAlbum.songs.add(song)
}
unknownAlbum.numSongs = unknownAlbum.songs.size
unknownAlbum.finalize()
albums.add(unknownAlbum)
@ -63,8 +63,6 @@ class MusicSorter(
"${unknownSongs.size} songs were placed into an unknown album."
)
}
albums.sortByDescending { it.title }
}
private fun sortAlbumsIntoArtists() {
@ -107,8 +105,6 @@ class MusicSorter(
"${unknownAlbums.size} albums were placed into an unknown artist."
)
}
artists.sortByDescending { it.name }
}
private fun sortArtistsIntoGenres() {
@ -126,7 +122,7 @@ class MusicSorter(
// Then add them to the genre, along with refreshing the amount of artists
genre.artists.addAll(genreArtists)
genre.numArtists = artists.size
genre.finalize()
unknownArtists.removeAll(genreArtists)
}
@ -140,7 +136,7 @@ class MusicSorter(
unknownGenre.artists.add(artist)
}
unknownGenre.numArtists = artists.size
unknownGenre.finalize()
genres.add(unknownGenre)
@ -149,14 +145,19 @@ class MusicSorter(
"${unknownArtists.size} albums were placed into an unknown genre."
)
}
genres.sortByDescending { it.name }
}
// 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 }
albums.forEach { if (it.name == "") it.name = albumPlaceholder }
}
// Sort all music into
private fun finalizeMusic() {
genres.sortBy { it.name }
artists.sortBy { it.name }
albums.sortBy { it.name }
}
}

View file

@ -32,7 +32,7 @@ class SongsFragment : Fragment() {
binding.songRecycler.adapter = SongAdapter(
songsModel.songs.value!!,
ClickListener { song ->
Log.d(this::class.simpleName, song.title)
Log.d(this::class.simpleName, song.name)
}
)
binding.songRecycler.applyDivider()

View file

@ -34,7 +34,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:text="@{album.title}"
android:text="@{album.name}"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="end"

View file

@ -42,7 +42,7 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
app:albumSongCount="@{artist}"
app:artistCounts="@{artist}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_name"

View file

@ -36,7 +36,7 @@
android:layout_marginStart="@dimen/margin_medium"
android:ellipsize="end"
android:maxLines="1"
android:text="@{song.title}"
android:text="@{song.name}"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/song_info"
@ -55,7 +55,7 @@
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:text="@{@string/format_song_info(song.album.artist.name, song.album.title)}"
android:text="@{@string/format_song_info(song.album.artist.name, song.album.name)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/duration"
app:layout_constraintStart_toEndOf="@+id/cover"

View file

@ -11,15 +11,21 @@
<string name="label_retry">Retry</string>
<string name="label_grant">Grant</string>
<string name="label_single_song">1 Song</string>
<string name="label_single_album">1 Album</string>
<string name="placeholder_unknown_genre">Unknown Genre</string>
<string name="placeholder_unknown_artist">Unknown Artist</string>
<string name="placeholder_unknown_album">Unknown Album</string>
<string name="format_multi_song_count">%s Songs</string>
<string name="format_song_info">%s / %s</string>
<string name="format_album_count">%s Albums</string>
<string name="format_combined_song_album">%s, %s</string>
<string name="format_song_info">%1$s / %2$s</string>
<string name="format_double_counts">%1$s, %2$s</string>
<plurals name="format_song_count">
<item quantity="one">%s Song</item>
<item quantity="other">%s Songs</item>
</plurals>
<plurals name="format_albums">
<item quantity="one">%s Album</item>
<item quantity="other">%s Albums</item>
</plurals>
</resources>