Refactor genre UI

Fully refactor the genre UI so that it makes more sense.
This commit is contained in:
OxygenCobalt 2020-12-21 20:10:38 -07:00
parent 6e5bff3bd3
commit 51a5e9fd63
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
20 changed files with 116 additions and 101 deletions

View file

@ -84,7 +84,8 @@ class ArtistDetailFragment : DetailFragment() {
true
}
R.id.action_play -> {
R.id.action_play_albums -> {
playbackModel.playArtist(
detailModel.currentArtist.value!!, false
)

View file

@ -16,7 +16,7 @@ import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setupSongActions
import org.oxycblt.auxio.ui.setupGenreSongActions
/**
* The [DetailFragment] for a genre.
@ -51,7 +51,7 @@ class GenreDetailFragment : DetailFragment() {
playbackModel.playSong(it, PlaybackMode.IN_GENRE)
},
doOnLongClick = { data, view ->
PopupMenu(requireContext(), view).setupSongActions(
PopupMenu(requireContext(), view).setupGenreSongActions(
requireContext(), data, playbackModel
)
}
@ -78,13 +78,6 @@ class GenreDetailFragment : DetailFragment() {
true
}
R.id.action_play -> {
playbackModel.playGenre(
detailModel.currentGenre.value!!, false
)
true
}
else -> false
}

View file

@ -161,12 +161,12 @@ data class Genre(
private val mSongs = mutableListOf<Song>()
val songs: List<Song> get() = mSongs
val albumCount: Int by lazy {
songs.groupBy { it.album }.size
}
val artistCount: Int by lazy {
songs.groupBy { it.album.artist }.size
val totalDuration: String by lazy {
var seconds: Long = 0
songs.forEach {
seconds += it.seconds
}
seconds.toDuration()
}
fun addSong(song: Song) {

View file

@ -105,37 +105,6 @@ fun Int.toYear(context: Context): String {
// --- BINDING ADAPTERS ---
/**
* Bind the artist + album counts for a genre
*/
@BindingAdapter("genreCounts")
fun TextView.bindGenreCounts(genre: Genre) {
val artists = context.resources.getQuantityString(
R.plurals.format_artist_count, genre.artistCount, genre.artistCount
)
val albums = context.resources.getQuantityString(
R.plurals.format_album_count, genre.albumCount, genre.albumCount
)
text = context.getString(R.string.format_double_counts, artists, albums)
}
/**
* Bind the album + song counts for a genre
*/
@BindingAdapter("altGenreCounts")
fun TextView.bindAltGenreCounts(genre: Genre) {
val albums = context.resources.getQuantityString(
R.plurals.format_album_count, genre.albumCount, genre.albumCount
)
val songs = context.resources.getQuantityString(
R.plurals.format_song_count, genre.songs.size, genre.songs.size
)
text = context.getString(R.string.format_double_counts, albums, songs)
}
/**
* Bind the most prominent artist genre
*/

View file

@ -2,6 +2,7 @@ package org.oxycblt.auxio.music.processing
import android.annotation.SuppressLint
import android.app.Application
import android.provider.MediaStore
import android.provider.MediaStore.Audio.Albums
import android.provider.MediaStore.Audio.Genres
import android.provider.MediaStore.Audio.Media
@ -16,7 +17,8 @@ import org.oxycblt.auxio.music.toAlbumArtURI
import org.oxycblt.auxio.music.toNamedGenre
/**
* Object that loads music from the filesystem.
* Class that loads/constructs [Genre]s, [Album]s, and [Song] objects from the filesystem
* Artists are constructed in [MusicSorter], as they are only really containers for [Album]s
*/
class MusicLoader(private val app: Application) {
var genres = mutableListOf<Genre>()
@ -117,10 +119,14 @@ class MusicLoader(private val app: Application) {
while (cursor.moveToNext()) {
val id = cursor.getLong(idIndex)
val name = cursor.getString(nameIndex) ?: albumPlaceholder
val artistName = cursor.getString(artistIdIndex) ?: artistPlaceholder
var artistName = cursor.getString(artistIdIndex) ?: artistPlaceholder
val year = cursor.getInt(yearIndex)
val coverUri = id.toAlbumArtURI()
if (artistName == MediaStore.UNKNOWN_STRING) {
artistName = artistPlaceholder
}
albums.add(
Album(
id = id, name = name, artistName = artistName,
@ -192,9 +198,7 @@ class MusicLoader(private val app: Application) {
for (genre in genres) {
val songGenreCursor = resolver.query(
Genres.Members.getContentUri("external", genre.id),
arrayOf(
Genres.Members._ID
),
arrayOf(Genres.Members._ID),
null, null, null
)

View file

@ -1,10 +1,12 @@
package org.oxycblt.auxio.music.processing
import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Song
/**
* Object responsible for creating [Artist]s from [Album]s and generally sorting everything.
*/
class MusicSorter(
val songs: MutableList<Song>,
val albums: MutableList<Album>
@ -27,7 +29,6 @@ class MusicSorter(
val groupedAlbums = albums.groupBy { it.artistName }
groupedAlbums.forEach {
logD(it.key)
artists.add(
Artist(id = artists.size.toLong(), name = it.key, albums = it.value)
)

View file

@ -507,15 +507,13 @@ class PlaybackStateManager private constructor() {
* @param useLastSong (Optional, defaults to false) Whether to use the previous song for the index calculations caused by the above parameter.
*/
private fun genShuffle(keepSong: Boolean, useLastSong: Boolean = false) {
val newSeed = Random.Default.nextLong()
mShuffleSeed = Random.Default.nextLong()
logD("Shuffling queue with seed $newSeed")
logD("Shuffling queue with seed $mShuffleSeed")
val lastSong = if (useLastSong) mQueue[mIndex] else mSong
mShuffleSeed = newSeed
mQueue.shuffle(Random(newSeed))
mQueue.shuffle(Random(mShuffleSeed))
mIndex = 0
// If specified, make the current song the first member of the queue.

View file

@ -10,6 +10,7 @@ import org.oxycblt.auxio.ui.ACCENTS
/**
* Wrapper around the [SharedPreferences] class that writes & reads values without a context.
* TODO: Add option to play song from genre, now that its possible
* @author OxygenCobalt
*/
class SettingsManager private constructor(context: Context) :

View file

@ -197,7 +197,7 @@ fun PopupMenu.setupAlbumActions(
else -> false
}
}
inflateAndShow(R.menu.menu_album_actions)
inflateAndShow(R.menu.menu_album_detail)
}
/**
@ -221,7 +221,7 @@ fun PopupMenu.setupArtistActions(artist: Artist, playbackModel: PlaybackViewMode
else -> false
}
}
inflateAndShow(R.menu.menu_detail)
inflateAndShow(R.menu.menu_artist_detail)
}
/**
@ -232,11 +232,6 @@ fun PopupMenu.setupArtistActions(artist: Artist, playbackModel: PlaybackViewMode
fun PopupMenu.setupGenreActions(genre: Genre, playbackModel: PlaybackViewModel) {
setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_play -> {
playbackModel.playGenre(genre, false)
true
}
R.id.action_shuffle -> {
playbackModel.playGenre(genre, true)
true
@ -245,7 +240,43 @@ fun PopupMenu.setupGenreActions(genre: Genre, playbackModel: PlaybackViewModel)
else -> false
}
}
inflateAndShow(R.menu.menu_detail)
inflateAndShow(R.menu.menu_artist_detail)
}
/**
* Show actions for a song in a genre.
* @param context [Context] required
* @param song [Song] the menu should correspond to
* @param playbackModel [PlaybackViewModel] to dispatch actions to
*/
fun PopupMenu.setupGenreSongActions(context: Context, song: Song, playbackModel: PlaybackViewModel) {
setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_queue_add -> {
playbackModel.addToUserQueue(song)
context.getString(R.string.label_queue_added).createToast(context)
true
}
R.id.action_play_artist -> {
playbackModel.playSong(song, PlaybackMode.IN_ARTIST)
true
}
R.id.action_play_album -> {
playbackModel.playSong(song, PlaybackMode.IN_ALBUM)
true
}
R.id.action_play_all_songs -> {
playbackModel.playSong(song, PlaybackMode.ALL_SONGS)
true
}
else -> false
}
}
inflateAndShow(R.menu.menu_genre_song_actions)
}
/**

View file

@ -29,7 +29,7 @@
style="@style/Toolbar.Style.Icon"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_album_actions"
app:menu="@menu/menu_album_detail"
app:title="@string/label_library" />
<androidx.core.widget.NestedScrollView

View file

@ -29,7 +29,7 @@
style="@style/Toolbar.Style.Icon"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_detail"
app:menu="@menu/menu_artist_detail"
app:title="@string/label_library" />
<androidx.core.widget.NestedScrollView

View file

@ -29,7 +29,7 @@
style="@style/Toolbar.Style.Icon"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_detail"
app:menu="@menu/menu_songs"
app:title="@string/label_library" />
<androidx.core.widget.NestedScrollView
@ -61,7 +61,7 @@
android:layout_marginStart="@dimen/margin_medium"
android:layout_marginEnd="@dimen/margin_medium"
android:text="@{genre.name}"
app:layout_constraintBottom_toTopOf="@+id/genre_counts"
app:layout_constraintBottom_toTopOf="@+id/genre_song_count"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/genre_image"
@ -70,30 +70,30 @@
tools:text="Genre Name" />
<TextView
android:id="@+id/genre_counts"
android:id="@+id/genre_song_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
app:genreCounts="@{genre}"
app:layout_constraintBottom_toTopOf="@+id/genre_song_count"
android:text="@{@plurals/format_song_count(genre.songs.size(), genre.songs.size())}"
app:layout_constraintBottom_toTopOf="@+id/genre_duration"
app:layout_constraintStart_toEndOf="@+id/genre_image"
app:layout_constraintTop_toBottomOf="@+id/genre_name"
tools:text="2 Artists, 4 Albums" />
<TextView
android:id="@+id/genre_song_count"
android:id="@+id/genre_duration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:text="@{@plurals/format_song_count(genre.songs.size, genre.songs.size)}"
android:text="@{genre.totalDuration}"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toTopOf="@+id/genre_song_header"
app:layout_constraintStart_toEndOf="@+id/genre_image"
app:layout_constraintTop_toBottomOf="@+id/genre_counts"
tools:text="80 Songs" />
app:layout_constraintTop_toBottomOf="@+id/genre_song_count"
tools:text="16:16" />
<TextView
android:id="@+id/genre_song_header"

View file

@ -29,7 +29,7 @@
style="@style/Toolbar.Style.Icon"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_album_actions"
app:menu="@menu/menu_album_detail"
app:title="@string/label_library" />
<androidx.core.widget.NestedScrollView

View file

@ -29,7 +29,7 @@
style="@style/Toolbar.Style.Icon"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_detail"
app:menu="@menu/menu_artist_detail"
app:title="@string/label_library" />
<androidx.core.widget.NestedScrollView

View file

@ -29,7 +29,7 @@
style="@style/Toolbar.Style.Icon"
android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_detail"
app:menu="@menu/menu_songs"
app:title="@string/label_library" />
<androidx.core.widget.NestedScrollView
@ -70,29 +70,29 @@
app:layout_constraintTop_toBottomOf="@+id/genre_image"
tools:text="Genre Name" />
<TextView
android:id="@+id/genre_counts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
app:genreCounts="@{genre}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_name"
tools:text="2 Artists, 4 Albums" />
<TextView
android:id="@+id/genre_song_count"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:text="@{@plurals/format_song_count(genre.songs.size, genre.songs.size)}"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
android:text="@{@plurals/format_song_count(genre.songs.size(), genre.songs.size())}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_name"
tools:text="80 Songs" />
<TextView
android:id="@+id/genre_duration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:text="@{genre.totalDuration}"
android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_counts"
tools:text="80 Songs" />
app:layout_constraintTop_toBottomOf="@+id/genre_song_count"
tools:text="16:16" />
<TextView
android:id="@+id/genre_song_header"
@ -100,7 +100,7 @@
android:layout_marginTop="@dimen/padding_medium"
android:text="@string/label_songs"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_song_count" />
app:layout_constraintTop_toBottomOf="@+id/genre_duration" />
<ImageButton
android:id="@+id/genre_sort_button"

View file

@ -38,12 +38,12 @@
<TextView
android:id="@+id/genre_count"
style="@style/ItemText.Secondary"
app:altGenreCounts="@{genre}"
android:text="@{@plurals/format_song_count(genre.songs.size(), genre.songs.size())}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/genre_image"
app:layout_constraintTop_toBottomOf="@+id/genre_name"
tools:text="4 Albums, 40 Songs" />
tools:text="40 Songs" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -7,8 +7,8 @@
android:title="@string/label_shuffle"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_play"
android:id="@+id/action_play_albums"
android:icon="@drawable/ic_play"
android:title="@string/label_play"
android:title="@string/label_play_albums"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_queue_add"
android:icon="@drawable/ic_queue_add"
android:title="@string/label_queue_add" />
<item
android:id="@+id/action_play_artist"
android:icon="@drawable/ic_artist"
android:title="@string/label_play_artist" />
<item
android:id="@+id/action_play_album"
android:icon="@drawable/ic_album"
android:title="@string/label_play_album" />
</menu>

View file

@ -28,6 +28,8 @@
<string name="label_play_album">Play from album</string>
<string name="label_play_artist">Play from artist</string>
<string name="label_go_artist">Go to artist</string>
<string name="label_play_albums">Play albums</string>
<string name="label_shuffle_albums">Shuffle albums</string>
<string name="label_queue">Queue</string>
<string name="label_queue_add">Add to queue</string>