Improve genre loading

Temporarily remove the ability to play from a genre, update MusicLoader to not load genres/artists simultaniously so that Artist ID's arent completely garbled. Improve PlaybackFragment slightly.
This commit is contained in:
OxygenCobalt 2020-10-15 08:49:32 -06:00
parent c17666752b
commit 339100e436
12 changed files with 81 additions and 154 deletions

View file

@ -121,13 +121,13 @@ class AlbumDetailFragment : Fragment() {
}
// Update the play button depending on the current playback status
// If the shown album is currently playing, set the button icon to the current isPlaying
// status, and then set its behavior to modify isPlaying.
// If the shown album isn't currently playing, set the button to Play and its behavior
// to start the playback of the album.
// If playing this album -> Make button show media controls
// If not playing this album -> Make button update playback to the artist
private fun updatePlayButton(mode: PlaybackMode, binding: FragmentAlbumDetailBinding) {
playbackModel.currentSong.value?.let { song ->
if (mode == PlaybackMode.IN_ALBUM && song.album == detailModel.currentAlbum.value) {
playbackModel.currentParent.value?.let { parent ->
if (mode == PlaybackMode.IN_ALBUM &&
parent.id == detailModel.currentAlbum.value!!.id
) {
if (playbackModel.isPlaying.value!!) {
binding.albumPlay.setImageResource(R.drawable.ic_pause)
} else {

View file

@ -106,14 +106,12 @@ class ArtistDetailFragment : Fragment() {
}
// Update the play button depending on the current playback status
// If the shown artist is currently playing, set the button icon to the current isPlaying
// status, and then set its behavior to modify isPlaying.
// If the shown artist isn't currently playing, set the button to Play and its behavior
// to start the playback of the artist.
// If playing this artist -> Make button show media controls
// If not playing this artist -> Make button update playback to the artist
private fun updatePlayButton(mode: PlaybackMode, binding: FragmentArtistDetailBinding) {
playbackModel.currentSong.value?.let { song ->
playbackModel.currentParent.value?.let { parent ->
if (mode == PlaybackMode.IN_ARTIST &&
song.album.artist == detailModel.currentArtist.value
parent.id == detailModel.currentArtist.value!!.id
) {
if (playbackModel.isPlaying.value!!) {
binding.artistPlay.setImageResource(R.drawable.ic_pause)

View file

@ -9,12 +9,9 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentGenreDetailBinding
import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackMode
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.disable
@ -22,7 +19,6 @@ class GenreDetailFragment : Fragment() {
private val args: GenreDetailFragmentArgs by navArgs()
private val detailModel: DetailViewModel by activityViewModels()
private val playbackModel: PlaybackViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
@ -57,7 +53,6 @@ class GenreDetailFragment : Fragment() {
binding.lifecycleOwner = this
binding.detailModel = detailModel
binding.playbackModel = playbackModel
binding.genre = detailModel.currentGenre.value
binding.genreToolbar.setNavigationOnClickListener {
@ -89,14 +84,6 @@ class GenreDetailFragment : Fragment() {
)
}
playbackModel.currentMode.observe(viewLifecycleOwner) {
updatePlayButton(it, binding)
}
playbackModel.isPlaying.observe(viewLifecycleOwner) {
updatePlayButton(playbackModel.currentMode.value!!, binding)
}
Log.d(this::class.simpleName, "Fragment created.")
return binding.root
@ -107,27 +94,4 @@ class GenreDetailFragment : Fragment() {
detailModel.updateNavigationStatus(false)
}
private fun updatePlayButton(mode: PlaybackMode, binding: FragmentGenreDetailBinding) {
if (mode == PlaybackMode.IN_GENRE &&
detailModel.currentGenre.value == playbackModel.currentGenre.value
) {
Log.d(this::class.simpleName, "Retard")
if (playbackModel.isPlaying.value!!) {
binding.genrePlay.setImageResource(R.drawable.ic_pause)
} else {
binding.genrePlay.setImageResource(R.drawable.ic_play)
}
binding.genrePlay.setOnClickListener {
playbackModel.invertPlayingStatus()
}
} else {
binding.genrePlay.setImageResource(R.drawable.ic_play)
binding.genrePlay.setOnClickListener {
playbackModel.play(detailModel.currentGenre.value!!, false)
}
}
}
}

View file

@ -31,7 +31,7 @@ data class Song(
data class Album(
override val id: Long = -1,
override val name: String,
val artistName: String,
val artistId: Long = -1,
val coverUri: Uri = Uri.EMPTY,
val year: Int = 0
) : BaseModel() {
@ -52,8 +52,7 @@ data class Album(
// Artist
data class Artist(
override val id: Long = -1,
override var name: String,
val givenGenres: MutableList<Genre> = mutableListOf()
override var name: String
) : BaseModel() {
val albums = mutableListOf<Album>()
val genres = mutableListOf<Genre>()
@ -75,14 +74,6 @@ data class Artist(
}
return songs
}
val genreSongs: MutableList<Song>
get() {
val songs = mutableListOf<Song>()
genres.forEach {
songs.addAll(it.songs)
}
return songs
}
}
// Genre
@ -109,14 +100,6 @@ data class Genre(
}
return num
}
val songs: MutableList<Song>
get() {
val songs = mutableListOf<Song>()
artists.forEach {
songs.addAll(it.songs)
}
return songs
}
}
// Header [Used for search, nothing else]

View file

@ -1,7 +1,7 @@
package org.oxycblt.auxio.music.processing
import android.content.ContentResolver
import android.database.Cursor
import android.provider.MediaStore
import android.provider.MediaStore.Audio.Albums
import android.provider.MediaStore.Audio.Artists
import android.provider.MediaStore.Audio.Genres
@ -32,11 +32,6 @@ class MusicLoader(
var albums = mutableListOf<Album>()
var songs = mutableListOf<Song>()
private var genreCursor: Cursor? = null
private var artistCursor: Cursor? = null
private var albumCursor: Cursor? = null
private var songCursor: Cursor? = null
val response: MusicLoaderResponse
init {
@ -67,7 +62,7 @@ class MusicLoader(
Log.d(this::class.simpleName, "Starting genre search...")
// First, get a cursor for every genre in the android system
genreCursor = resolver.query(
val genreCursor = resolver.query(
Genres.EXTERNAL_CONTENT_URI,
arrayOf(
Genres._ID, // 0
@ -86,7 +81,7 @@ class MusicLoader(
val id = cursor.getLong(idIndex)
var name = cursor.getString(nameIndex) ?: genrePlaceholder
// If a genre is still in an old int-based format [Android formats it as "(INT)"],
// If a genre is still in an old int-based format [Android formats it as "(INT)"],mu
// convert that to the corresponding ID3 genre.
if (name.contains(Regex("[0123456789)]"))) {
name = name.toNamedGenre() ?: genrePlaceholder
@ -111,42 +106,59 @@ class MusicLoader(
private fun loadArtists() {
Log.d(this::class.simpleName, "Starting artist search...")
// Iterate through the artists for each loaded genre, and then add the genre
// with the artist.
// This is only done because using GENRE_NAME for songs is broken and has been for years.
// Load all the artists
val artistCursor = resolver.query(
Artists.EXTERNAL_CONTENT_URI,
arrayOf(
Artists._ID, // 0
Artists.ARTIST // 1
),
null, null,
Artists.DEFAULT_SORT_ORDER
)
artistCursor?.use { cursor ->
val idIndex = cursor.getColumnIndexOrThrow(Artists._ID)
val nameIndex = cursor.getColumnIndexOrThrow(Artists.ARTIST)
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
var name = cursor.getString(nameIndex)
if (name == null || name == MediaStore.UNKNOWN_STRING) {
name = artistPlaceholder
}
Log.d(this::class.simpleName, id.toString())
artists.add(
Artist(
id, name
)
)
}
cursor.close()
}
// Then try to associate any genres with their respective artists.
for (genre in genres) {
artistCursor = resolver.query(
val artistGenreCursor = resolver.query(
Genres.Members.getContentUri("external", genre.id),
arrayOf(
Artists._ID, // 0
Artists.ARTIST // 1
Genres.Members.ARTIST_ID
),
null, null,
Artists.DEFAULT_SORT_ORDER
null, null, null
)
artistCursor?.use { cursor ->
val idIndex = cursor.getColumnIndexOrThrow(Artists._ID)
val nameIndex = cursor.getColumnIndexOrThrow(Artists.ARTIST)
artistGenreCursor?.let { cursor ->
val idIndex = cursor.getColumnIndexOrThrow(Genres.Members.ARTIST_ID)
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
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
// new one.
val existingArtist = artists.find { it.name == name }
if (existingArtist != null) {
existingArtist.givenGenres.add(genre)
} else {
artists.add(
Artist(
id, name,
mutableListOf(genre)
)
)
artists.filter { it.id == id }.forEach {
it.genres.add(genre)
}
}
@ -154,9 +166,8 @@ class MusicLoader(
}
}
// Remove dupes [Just in case]
artists = artists.distinctBy {
it.name to it.givenGenres
it.name to it.genres
}.toMutableList()
Log.d(
@ -168,12 +179,12 @@ class MusicLoader(
private fun loadAlbums() {
Log.d(this::class.simpleName, "Starting album search...")
albumCursor = resolver.query(
val albumCursor = resolver.query(
Albums.EXTERNAL_CONTENT_URI,
arrayOf(
Albums._ID, // 0
Albums.ALBUM, // 1
Albums.ARTIST, // 2
Albums.ARTIST_ID, // 2
Albums.FIRST_YEAR, // 3
),
@ -184,20 +195,20 @@ class MusicLoader(
albumCursor?.use { cursor ->
val idIndex = cursor.getColumnIndexOrThrow(Albums._ID)
val nameIndex = cursor.getColumnIndexOrThrow(Albums.ALBUM)
val artistIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST)
val artistIdIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST_ID)
val yearIndex = cursor.getColumnIndexOrThrow(Albums.FIRST_YEAR)
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
val name = cursor.getString(nameIndex) ?: albumPlaceholder
val artist = cursor.getString(artistIndex) ?: artistPlaceholder
val artistId = cursor.getLong(artistIdIndex)
val year = cursor.getInt(yearIndex)
val coverUri = id.toAlbumArtURI()
albums.add(
Album(
id, name, artist,
id, name, artistId,
coverUri, year
)
)
@ -206,9 +217,8 @@ class MusicLoader(
cursor.close()
}
// Remove dupes
albums = albums.distinctBy {
it.name to it.artistName to it.year to it.numSongs
it.name to it.artistId to it.year to it.numSongs
}.toMutableList()
Log.d(
@ -220,7 +230,7 @@ class MusicLoader(
private fun loadSongs() {
Log.d(this::class.simpleName, "Starting song search...")
songCursor = resolver.query(
val songCursor = resolver.query(
Media.EXTERNAL_CONTENT_URI,
arrayOf(
Media._ID, // 0
@ -260,7 +270,6 @@ class MusicLoader(
cursor.close()
}
// Remove dupes
songs = songs.distinctBy {
it.name to it.albumId to it.track to it.duration
}.toMutableList()

View file

@ -51,8 +51,7 @@ class MusicSorter(
if (unknownSongs.size > 0) {
val unknownAlbum = Album(
name = albumPlaceholder,
artistName = artistPlaceholder
name = albumPlaceholder
)
for (song in unknownSongs) {
@ -76,7 +75,7 @@ class MusicSorter(
for (artist in artists) {
// Find all albums that match the current artist name
val artistAlbums = albums.filter { it.artistName == artist.name }
val artistAlbums = albums.filter { it.artistId == artist.id }
// Then add them to the artist, along with refreshing the amount of albums
for (album in artistAlbums) {
@ -85,8 +84,9 @@ class MusicSorter(
}
// Then group the artist's genres and sort them by "Prominence"
// A.K.A Who has the most map entries
val groupedGenres = artist.givenGenres.groupBy { it.name }
// A.K.A Who has the most bugged duplicate genres
val groupedGenres = artist.genres.groupBy { it.name }
artist.genres.clear()
groupedGenres.keys.sortedByDescending { key ->
groupedGenres[key]?.size
@ -129,7 +129,7 @@ class MusicSorter(
for (genre in genres) {
// Find all artists that match the current genre
val genreArtists = artists.filter { artist ->
artist.givenGenres.any {
artist.genres.any {
it.name == genre.name
}
}

View file

@ -6,7 +6,7 @@ package org.oxycblt.auxio.playback
// IN_ARTIST -> Play from the songs of the artist
// IN_ALBUM -> Play from the songs of the album
enum class PlaybackMode {
IN_GENRE, IN_ARTIST, IN_ALBUM, ALL_SONGS;
IN_ARTIST, IN_ALBUM, ALL_SONGS;
// Make a slice of all the values that this ShowMode covers.
// ex. SHOW_ARTISTS would return SHOW_ARTISTS, SHOW_ALBUMS, and SHOW_SONGS

View file

@ -7,6 +7,7 @@ import androidx.lifecycle.Transformations
import androidx.lifecycle.ViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song
@ -21,8 +22,8 @@ class PlaybackViewModel : ViewModel() {
private val mCurrentSong = MutableLiveData<Song>()
val currentSong: LiveData<Song> get() = mCurrentSong
private val mCurrentGenre = MutableLiveData<Genre>()
val currentGenre: LiveData<Genre> get() = mCurrentGenre
private val mCurrentParent = MutableLiveData<BaseModel>()
val currentParent: LiveData<BaseModel> get() = mCurrentParent
private val mQueue = MutableLiveData(mutableListOf<Song>())
val queue: LiveData<MutableList<Song>> get() = mQueue
@ -62,19 +63,6 @@ class PlaybackViewModel : ViewModel() {
PlaybackMode.ALL_SONGS -> musicStore.songs.toMutableList()
PlaybackMode.IN_ARTIST -> song.album.artist.songs
PlaybackMode.IN_ALBUM -> song.album.songs
// Warning: Calling update() with a mode of IN_GENRE Will cause Auxio to play
// from the artist's most prominent genre instead of the song's genre.
// FIXME: This could be fixed by moving genre loading to songs
PlaybackMode.IN_GENRE -> {
Log.d(
this::class.simpleName,
"update() was called with IN_GENRES, using " +
"most prominent genre instead of the song's genre."
)
song.album.artist.genres[0].songs
}
}
mCurrentMode.value = mode
@ -90,6 +78,7 @@ class PlaybackViewModel : ViewModel() {
mQueue.value = songs
mCurrentIndex.value = 0
mCurrentParent.value = album
mCurrentMode.value = PlaybackMode.IN_ALBUM
}
@ -102,6 +91,7 @@ class PlaybackViewModel : ViewModel() {
mQueue.value = songs
mCurrentIndex.value = 0
mCurrentParent.value = artist
mCurrentMode.value = PlaybackMode.IN_ARTIST
}
@ -112,10 +102,8 @@ class PlaybackViewModel : ViewModel() {
updatePlayback(songs[0])
mCurrentGenre.value = genre
mQueue.value = songs
mCurrentIndex.value = 0
mCurrentMode.value = PlaybackMode.IN_GENRE
}
private fun updatePlayback(song: Song) {

View file

@ -161,6 +161,7 @@
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/album_song_header"
tools:itemCount="4"
tools:listitem="@layout/item_album_song" />

View file

@ -158,6 +158,7 @@
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_album_header"
tools:itemCount="4"
tools:listitem="@layout/item_album" />

View file

@ -99,23 +99,6 @@
app:layout_constraintTop_toBottomOf="@+id/genre_counts"
tools:text="80 Songs" />
<ImageButton
android:id="@+id/genre_play"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="@dimen/margin_tiny"
android:layout_marginEnd="@dimen/margin_medium"
android:background="@drawable/ui_circular_button"
android:backgroundTint="?android:attr/colorPrimary"
android:contentDescription="@string/description_play"
android:src="@drawable/ic_play"
android:onClick="@{() -> playbackModel.play(genre, false)}"
app:layout_constraintBottom_toBottomOf="@+id/genre_song_count"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/genre_counts" />
<TextView
android:id="@+id/genre_artist_header"
android:layout_width="match_parent"
@ -159,6 +142,7 @@
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_artist_header"
tools:itemCount="4"
tools:listitem="@layout/item_artist" />

View file

@ -41,7 +41,6 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="@dimen/margin_mid_large"
android:elevation="4dp"
android:contentDescription="@{@string/description_album_cover(song.name)}"
app:coverArt="@{song}"
app:layout_constraintBottom_toTopOf="@+id/playback_song"