diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 57dbef8f4..d4e062203 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -12,7 +12,6 @@ import androidx.navigation.fragment.navArgs import org.oxycblt.auxio.ClickListener import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter -import org.oxycblt.auxio.library.DetailViewModel import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.models.Album import org.oxycblt.auxio.theme.applyDivider diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 52db36004..3475ed813 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -1,4 +1,4 @@ -package org.oxycblt.auxio.library +package org.oxycblt.auxio.detail import androidx.lifecycle.ViewModel diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt index 9d42206b0..4bcef03bf 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt @@ -24,7 +24,7 @@ class DetailSongAdapter( holder.bind(data[position]) } - // Generic ViewHolder for an album + // Generic ViewHolder for a song inner class ViewHolder( private val binding: ItemAlbumSongBinding ) : RecyclerView.ViewHolder(binding.root) { diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt index b958b11cf..d5031f01b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt @@ -37,13 +37,13 @@ private val ID3_GENRES = arrayOf( const val PAREN_FILTER = "()" // Convert legacy ID3 genres to a named genre -fun String.toNamedGenre(): String { +fun String.toNamedGenre(): String? { // Strip the genres of any parentheses, and convert it to an int val intGenre = this.filterNot { PAREN_FILTER.indexOf(it) > -1 }.toInt() - return ID3_GENRES.getOrNull(intGenre) ?: "" + return ID3_GENRES.getOrNull(intGenre) } // Convert a song to its URI diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt index c2bc68537..c7d322d2e 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt @@ -69,20 +69,32 @@ class MusicViewModel(private val app: Application) : ViewModel() { ioScope.launch { val start = System.currentTimeMillis() - val loader = MusicLoader(app.contentResolver) + // Get the placeholder strings, which are used by MusicLoader & MusicSorter for + // any music that doesn't have metadata. + val genrePlaceholder = app.getString(R.string.placeholder_genre) + val artistPlaceholder = app.getString(R.string.placeholder_artist) + val albumPlaceholder = app.getString(R.string.placeholder_album) + + val loader = MusicLoader( + app.contentResolver, + + genrePlaceholder, + artistPlaceholder, + albumPlaceholder + ) withContext(Dispatchers.Main) { if (loader.response == MusicLoaderResponse.DONE) { - // If the loading succeeds, then process the songs and set them. + // If the loading succeeds, then sort the songs and update the value val sorter = MusicSorter( loader.genres, loader.artists, loader.albums, loader.songs, - app.getString(R.string.placeholder_unknown_genre), - app.getString(R.string.placeholder_unknown_artist), - app.getString(R.string.placeholder_unknown_album) + genrePlaceholder, + artistPlaceholder, + albumPlaceholder ) mSongs.value = sorter.songs.toList() diff --git a/app/src/main/java/org/oxycblt/auxio/music/models/Album.kt b/app/src/main/java/org/oxycblt/auxio/music/models/Album.kt index 1c215d536..254593620 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/models/Album.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/models/Album.kt @@ -4,9 +4,9 @@ import android.net.Uri // Abstraction for Song data class Album( - val id: Long = 0L, - var name: String = "", - val artistName: String = "", // only used for sorting. Use artist.name instead. + val id: Long = -1, + var name: String, + val artistName: String, val coverUri: Uri = Uri.EMPTY, val year: Int = 0 ) { diff --git a/app/src/main/java/org/oxycblt/auxio/music/models/Artist.kt b/app/src/main/java/org/oxycblt/auxio/music/models/Artist.kt index 9324b1057..28c3af925 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/models/Artist.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/models/Artist.kt @@ -2,9 +2,9 @@ package org.oxycblt.auxio.music.models // Abstraction for albums data class Artist( - val id: Long = 0, - var name: String = "", - val genres: MutableList = mutableListOf(Genre()) + val id: Long = -1, + var name: String, + val genres: MutableList = mutableListOf() ) { val albums = mutableListOf() var genre = "" diff --git a/app/src/main/java/org/oxycblt/auxio/music/models/Genre.kt b/app/src/main/java/org/oxycblt/auxio/music/models/Genre.kt index 60ca100d2..ee06b7929 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/models/Genre.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/models/Genre.kt @@ -1,8 +1,8 @@ package org.oxycblt.auxio.music.models data class Genre( - val id: Long = 0, - var name: String = "", + val id: Long = -1, + var name: String, ) { val artists = mutableListOf() diff --git a/app/src/main/java/org/oxycblt/auxio/music/models/Song.kt b/app/src/main/java/org/oxycblt/auxio/music/models/Song.kt index 4312098ee..5dcdc4d6b 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/models/Song.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/models/Song.kt @@ -6,12 +6,12 @@ import android.text.format.DateUtils data class Song( val id: Long, var name: String, - val albumName: String, // Only used for sorting. Use album.title for everything else. + val albumId: Long, val track: Int, val duration: Long ) { lateinit var album: Album val seconds = duration / 1000 - val formattedDuration = DateUtils.formatElapsedTime(seconds) + val formattedDuration: String = DateUtils.formatElapsedTime(seconds) } diff --git a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt index a001332d8..4d3e0658a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt @@ -19,7 +19,13 @@ enum class MusicLoaderResponse { } // Class that loads music from the FileSystem. -class MusicLoader(private val resolver: ContentResolver) { +class MusicLoader( + private val resolver: ContentResolver, + + private val genrePlaceholder: String, + private val artistPlaceholder: String, + private val albumPlaceholder: String, +) { var genres = mutableListOf() var artists = mutableListOf() @@ -78,12 +84,12 @@ class MusicLoader(private val resolver: ContentResolver) { while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) - var name = cursor.getString(nameIndex) ?: "" + var name = cursor.getString(nameIndex) ?: genrePlaceholder // If a genre is still in an old int-based format [Android formats it as "(INT)"], // convert that to the corresponding ID3 genre. if (name.contains(Regex("[0123456789)]"))) { - name = name.toNamedGenre() + name = name.toNamedGenre() ?: genrePlaceholder } genres.add( @@ -124,7 +130,7 @@ class MusicLoader(private val resolver: ContentResolver) { while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) - val name = cursor.getString(nameIndex) ?: "" + val name = cursor.getString(nameIndex) ?: artistPlaceholder // If an artist has already been added [Which is very likely due to how genres // are processed], add the genre to the existing artist instead of creating a @@ -182,12 +188,16 @@ class MusicLoader(private val resolver: ContentResolver) { while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) - val name = cursor.getString(nameIndex) ?: "" - val artist = cursor.getString(artistIndex) ?: "" + var name = cursor.getString(nameIndex) ?: albumPlaceholder + val artist = cursor.getString(artistIndex) val year = cursor.getInt(yearIndex) val coverUri = id.toAlbumArtURI() + // Sometimes the android system will return 0 for an album name, update + // it properly if that happens. + name = if (name == "0") albumPlaceholder else name + albums.add( Album( id, name, artist, @@ -219,9 +229,9 @@ class MusicLoader(private val resolver: ContentResolver) { Media._ID, // 0 Media.DISPLAY_NAME, // 1 Media.TITLE, // 2 - Media.ALBUM, // 4 - Media.TRACK, // 6 - Media.DURATION // 7 + Media.ALBUM_ID, // 3 + Media.TRACK, // 4 + Media.DURATION // 5 ), Media.IS_MUSIC + "=1", null, Media.DEFAULT_SORT_ORDER @@ -231,20 +241,20 @@ class MusicLoader(private val resolver: ContentResolver) { val idIndex = cursor.getColumnIndexOrThrow(Media._ID) val fileIndex = cursor.getColumnIndexOrThrow(Media.DISPLAY_NAME) val titleIndex = cursor.getColumnIndexOrThrow(Media.TITLE) - val albumIndex = cursor.getColumnIndexOrThrow(Media.ALBUM) + val albumIndex = cursor.getColumnIndexOrThrow(Media.ALBUM_ID) val trackIndex = cursor.getColumnIndexOrThrow(Media.TRACK) val durationIndex = cursor.getColumnIndexOrThrow(Media.DURATION) while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) val title = cursor.getString(titleIndex) ?: cursor.getString(fileIndex) - val album = cursor.getString(albumIndex) ?: "" + val albumId = cursor.getLong(albumIndex) val track = cursor.getInt(trackIndex) val duration = cursor.getLong(durationIndex) songs.add( Song( - id, title, album, + id, title, albumId, track, duration ) ) @@ -255,7 +265,7 @@ class MusicLoader(private val resolver: ContentResolver) { // Remove dupes songs = songs.distinctBy { - it.name to it.albumName to it.track to it.duration + it.name to it.albumId to it.track to it.duration }.toMutableList() Log.d( diff --git a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicSorter.kt b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicSorter.kt index a338b5d05..2de285b6a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicSorter.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicSorter.kt @@ -21,7 +21,6 @@ class MusicSorter( sortAlbumsIntoArtists() sortArtistsIntoGenres() - addPlaceholders() finalizeMusic() } @@ -31,8 +30,9 @@ class MusicSorter( val unknownSongs = songs.toMutableList() for (album in albums) { - // Find all songs that match the current album title - val albumSongs = songs.filter { it.albumName == album.name } + // Find all songs that match the current album ID to prevent any bugs w/comparing names. + // This cant be done with artists/genres sadly. + val albumSongs = songs.filter { it.albumId == album.id } // Then add them to the album for (song in albumSongs) { @@ -40,8 +40,6 @@ class MusicSorter( album.songs.add(song) } - album.finalize() - unknownSongs.removeAll(albumSongs) } @@ -49,15 +47,16 @@ class MusicSorter( if (unknownSongs.size > 0) { // Reuse an existing unknown album if one is found - val unknownAlbum = albums.find { it.name == "" } ?: Album() + val unknownAlbum = Album( + name = albumPlaceholder, + artistName = artistPlaceholder + ) for (song in unknownSongs) { song.album = unknownAlbum unknownAlbum.songs.add(song) } - unknownAlbum.finalize() - albums.add(unknownAlbum) Log.d( @@ -76,14 +75,14 @@ class MusicSorter( // Find all albums that match the current artist name val artistAlbums = albums.filter { it.artistName == artist.name } + Log.d(this::class.simpleName, artist.id.toString()) + // Then add them to the artist, along with refreshing the amount of albums for (album in artistAlbums) { album.artist = artist artist.albums.add(album) } - artist.finalize() - unknownAlbums.removeAll(artistAlbums) } @@ -91,15 +90,15 @@ class MusicSorter( if (unknownAlbums.size > 0) { // Reuse an existing unknown artist if one is found - val unknownArtist = artists.find { it.name == "" } ?: Artist() + val unknownArtist = Artist( + name = artistPlaceholder + ) for (album in unknownAlbums) { album.artist = unknownArtist unknownArtist.albums.add(album) } - unknownArtist.finalize() - artists.add(unknownArtist) Log.d( @@ -124,40 +123,39 @@ class MusicSorter( // Then add them to the genre, along with refreshing the amount of artists genre.artists.addAll(genreArtists) - genre.finalize() unknownArtists.removeAll(genreArtists) } if (unknownArtists.size > 0) { // Reuse an existing unknown genre if one is found - val unknownGenre = genres.find { it.name == "" } ?: Genre() + val unknownGenre = Genre( + name = genrePlaceholder + ) for (artist in unknownArtists) { artist.genres.add(unknownGenre) unknownGenre.artists.add(artist) } - - unknownGenre.finalize() - genres.add(unknownGenre) Log.d( this::class.simpleName, - "${unknownArtists.size} albums were placed into an unknown genre." + "${unknownArtists.size} artists 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.name == "") it.name = albumPlaceholder } - } - - // Sort all music + // Finalize music private fun finalizeMusic() { + // Correct any empty names [""] with the proper placeholders [Unknown Album] + genres.forEach { it.finalize() } + + artists.forEach { it.finalize() } + + albums.forEach { it.finalize() } + + // Then finally sort the music genres.sortWith( compareBy(String.CASE_INSENSITIVE_ORDER, { it.name }) ) diff --git a/app/src/main/res/layout/fragment_album_detail.xml b/app/src/main/res/layout/fragment_album_detail.xml index c4535b4cd..d3c31acb0 100644 --- a/app/src/main/res/layout/fragment_album_detail.xml +++ b/app/src/main/res/layout/fragment_album_detail.xml @@ -90,7 +90,7 @@ tools:text="2020" /> diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml index d14c81684..5e196394e 100644 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ b/app/src/main/res/layout/fragment_artist_detail.xml @@ -90,7 +90,7 @@ tools:text="2 Albums, 20 Songs" /> + app:layout_constraintTop_toBottomOf="@+id/artist_counts" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6e2a34c02..ea697d36d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,9 +14,9 @@ Albums Songs - Unknown Genre - Unknown Artist - Unknown Album + Unknown Genre + Unknown Artist + Unknown Album %1$s / %2$s %1$s, %2$s